/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   main.c                                             :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: baco <baco@student.42.fr>                  +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2022/04/13 06:58:46 by gbaconni          #+#    #+#             */
/*   Updated: 2022/04/18 09:12:07 by gbaconni         ###   lausanne.ch       */
/*                                                                            */
/* ************************************************************************** */

#include <stdio.h>  // printf
#include <stdarg.h> // va_list, va_start, va_end
#include <unistd.h> // write
#include <string.h> // strlen
#include <stdlib.h> // malloc, free
#include <ctype.h>  // isdigit
#include <assert.h> // assert
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h> // wait

#include "libftprintf/libftprintf.h"

void	ft_begin(int *fd)
{
	pipe(fd);
	fd[2] = dup(STDOUT_FILENO);
	dup2(fd[1], STDOUT_FILENO);
}

void	ft_end(int *fd, char *str)
{
	char	c; 

	dup2(fd[2], STDOUT_FILENO);
	fflush(stdout);
	close(fd[1]);
	while (read(fd[0], &c, 1) > 0)
		*str++ = c;
	*str = '\0';
	close(fd[0]);
}

void	ft_begin2(char *out)
{
	freopen("/dev/null", "a", stdout);
    setbuf(stdout, out);
}

void	ft_end2(void)
{
	freopen("/dev/tty", "a", stdout);
}

int ft_sprintf(char *str, const char *format, ...)
{
    va_list		ap;
    int			ret;
    int			state;
	char		c;
	pid_t		cpid;
	int			pipefd[3];
	const int	piperead = 0;
	const int	pipewrite = 1;
	const int	pipestdout = 2;

	ret = 0;
	pipe(pipefd);
	cpid = fork();
	if (cpid == 0)
	{
		close(pipefd[piperead]);
		pipefd[pipestdout] = dup(STDOUT_FILENO);
		dup2(pipefd[pipewrite], STDOUT_FILENO);
		va_start(ap, format);
		ret = ft_vprintf(format, ap);
		va_end(ap);
		fflush(stdout);
		dup2(pipefd[pipestdout], STDOUT_FILENO);
		close(pipefd[pipestdout]);
		close(pipefd[pipewrite]);
		exit (ret);
	} else {
		close(pipefd[pipewrite]);
		while (read(pipefd[piperead], &c, 1) > 0)
			*str++ = c;
		close(pipefd[piperead]);
		wait(&state);
		return (WEXITSTATUS(state));
	}
}

size_t	ft_unescape_len(const char *str)
{
	size_t	len;

	len = 0;
	while (*str != '\0')
	{
		if (*str == '\\')
			str++;
		len++;
		str++;
	}
	return (len);
}

char	ft_unescape_char(const char c)
{
	if (c == 'a')
		return ('\a');
	else if (c == 'b')
		return ('\b');
	else if (c == 't')
		return ('\t');
	else if (c == 'n')
		return ('\n');
	else if (c == 'v')
		return ('\v');
	else if (c == 'f')
		return ('\f');
	else if (c == 'r')
		return ('\r');
	else if (c == 'e')
		return ('\033');
	else
		return (c);
}

char	*ft_unescape(const char *str)
{
	char	*s;
	void	*ptr;

	s = (char *) ft_calloc(ft_unescape_len(str) + 1, sizeof(char));
	if (s == NULL)
		return (NULL);
	ptr = s;
	while (*str != '\0')
	{
		if (*str == '\\')
			*s++ = ft_unescape_char(*++str);
		else
			*s++ = *str;
		str++;
	}
	*s = '\0';
	s = ptr;
	return (s);
}

int	main(int argc, char *argv[])
{
	int		ft_ret;
	int		ret;
	char	out[256];
	char	ft_out[256];
	char	*format;
	char	*f;
	int		d;
	char	c;
	char	*s;
	int		x;
	void	*ptr;
	int		fd[3];

	d = 0;
	c = '\0';
	s = "";
	x = 42;
	ptr = &x;
	(void)s;
	if (argc > 1)
	{
		f = argv[1];
		format = ft_unescape(f);
        if (format[0] == '%')
        {
            if (format[1] == 'c')
			{
				c = argv[2][0];
				ret = sprintf(out, format, c);
				printf("%d = printf(\"%s\", '%c')\n%s\n", ret, f, c, out);
				ft_begin(fd);
				ft_ret = ft_printf(format, c);
				ft_end(fd, ft_out);
				printf("%d = ft_printf(\"%s\", '%c')\n%s\n\n", ft_ret, f, c, ft_out);
				assert(ret == ft_ret);
				assert(strcmp(out, ft_out) == 0);
			}
            else if (format[1] == 's')
			{
				s = argv[2];
				ret = sprintf(out, format, s);
				printf("%d = printf(\"%s\", \"%s\")\n%s\n", ret, f, s, out);
				ft_begin(fd);
				ft_ret = ft_printf(format, s);
				ft_end(fd, ft_out);
				printf("%d = ft_printf(\"%s\", \"%s\")\n%s\n\n", ft_ret, f, s, ft_out);
				assert(ret == ft_ret);
				assert(strcmp(out, ft_out) == 0);
			}
            else if (format[1] == 'p')
			{
				s = argv[2];
				if (ft_strlen(s) == 0)
				{
					s = NULL;
					ptr = NULL;
				}
				ret = sprintf(out, format, ptr);
				printf("%d = printf(\"%s\", %p)\n%s\n", ret, f, ptr, out);
				ft_begin(fd);
				ft_ret = ft_printf(format, ptr);
				ft_end(fd, ft_out);
				printf("%d = ft_printf(\"%s\", %p)\n%s\n\n", ft_ret, f, ptr, ft_out);
				assert(ret == ft_ret);
				assert(strcmp(out, ft_out) == 0);
			}
            else if (format[1] == 'd' || format[1] == 'i' || format[1] == 'u')
			{
				d = atoi(argv[2]);
				ret = sprintf(out, format, d);
				printf("%d = printf(\"%s\", %d)\n%s\n", ret, f, d, out);
				ft_begin(fd);
				ft_ret = ft_printf(format, d);
				ft_end(fd, ft_out);
				printf("%d = ft_printf(\"%s\", %d)\n%s\n\n", ft_ret, f, d, ft_out);
				assert(ret == ft_ret);
				assert(strcmp(out, ft_out) == 0);
			}
            else if (format[1] == 'x' || format[1] == 'X')
			{
				d = atoi(argv[2]);
				ret = sprintf(out, format, d);
				printf("%d = printf(\"%s\", %d)\n%s\n", ret, f, d, out);
				ft_begin(fd);
				ft_ret = ft_printf(format, d);
				ft_end(fd, ft_out);
				printf("%d = ft_printf(\"%s\", %d)\n%s\n\n", ft_ret, f, d, ft_out);
				assert(ret == ft_ret);
				assert(strcmp(out, ft_out) == 0);
			}
        }
        else
		{
			s = argv[2];
			ret = sprintf(out, format, s);
			printf("%d = printf(\"%s\", \"%s\")\n%s\n", ret, f, s, out);
			ft_begin(fd);
			ft_ret = ft_printf(format, s);
			ft_end(fd, ft_out);
			printf("%d = ft_printf(\"%s\", \"%s\")\n%s\n\n", ft_ret, f, s, ft_out);
			assert(ret == ft_ret);
			assert(strcmp(out, ft_out) == 0);
		}
		free(format);
	}
	return (0);
}
