#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <ctype.h>

#define MAX_CMD   1024
#define MAX_ARGS  64
#define MAX_PIPES 32

char *trim(char *s) {
    while (isspace((unsigned char)*s)) s++;
    if (*s == '\0') return s;
    char *end = s + strlen(s) - 1;
    while (end > s && isspace((unsigned char)*end)) end--;
    *(end + 1) = '\0';
    return s;
}

int parse_args(char *cmd, char **args) {
    int argc = 0;
    char *token = strtok(cmd, " \t");
    while (token != NULL && argc < MAX_ARGS - 1) {
        args[argc++] = token;
        token = strtok(NULL, " \t");
    }
    args[argc] = NULL;
    return argc;
}

int split_pipeline(char *buf, char *segments[], int max_segs) {
    int   nseg      = 0;
    char *seg_start = buf;   /* 当前段的起始位置 */
    char *p         = buf;

    while (*p != '\0') {
        if (*p == '|') {
            /* 找到管道符：截断当前段，保存去空格后的指针 */
            *p = '\0';
            char *seg = trim(seg_start);
            if (seg[0] != '\0') {          /* 跳过空段 */
                if (nseg >= max_segs) {
                    fprintf(stderr, "shell: too many pipes (max %d)\n", max_segs);
                    return -1;
                }
                segments[nseg++] = seg;
            }
            seg_start = p + 1;             /* 下一段从 '|' 后面开始 */
        }
        p++;
    }

    /* 保存最后一段（'|' 后面，或整个命令没有 '|' 时） */
    char *last = trim(seg_start);
    if (last[0] != '\0') {
        segments[nseg++] = last;
    }

    return nseg;
}

static void exec_cmd_in_child(char **args, int argc, int in_fd, int out_fd) {
    char *infile    = NULL;
    char *outfile   = NULL;
    char *clean_args[MAX_ARGS];
    int   clean_argc = 0;

    for (int i = 0; i < argc; i++) {
        if (strcmp(args[i], "<") == 0 && i + 1 < argc) {
            infile = args[++i];                  /* 记录输入文件，跳过文件名 */
        } else if (strcmp(args[i], ">") == 0 && i + 1 < argc) {
            outfile = args[++i];                 /* 记录输出文件，跳过文件名 */
        } else {
            clean_args[clean_argc++] = args[i];  /* 普通参数保留 */
        }
    }
    clean_args[clean_argc] = NULL;

    if (clean_argc == 0) 
    {
        exit(0);
    }

    if (infile) {
        if (in_fd != STDIN_FILENO) 
            close(in_fd); /* 管道读端不再需要，关掉 */

        int fd = open(infile, O_RDONLY);

        if (fd < 0) { 
            perror("open"); exit(1); 
        }
        dup2(fd, STDIN_FILENO);
        close(fd);
    } else if (in_fd != STDIN_FILENO) {
        dup2(in_fd, STDIN_FILENO);
        close(in_fd);
    }

    if (outfile) {
        if (out_fd != STDOUT_FILENO) 
            close(out_fd);
        int fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (fd < 0) { 
            perror("open"); exit(1); 
        }
        dup2(fd, STDOUT_FILENO);
        close(fd);
    } else if (out_fd != STDOUT_FILENO) {
        dup2(out_fd, STDOUT_FILENO);
        close(out_fd);
    }

    execvp(clean_args[0], clean_args);
    fprintf(stderr, "shell: command not found: %s\n", clean_args[0]);
    exit(127);
}

static void run_single(char *seg) {
    char  buf[MAX_CMD];
    strncpy(buf, seg, MAX_CMD - 1);
    buf[MAX_CMD - 1] = '\0';

    char *args[MAX_ARGS];
    int   argc = parse_args(buf, args);
    if (argc == 0) return;

    pid_t pid = fork();
    if (pid < 0) { perror("fork"); return; }
    if (pid == 0) {
        /* 子进程：从终端 stdin 读，输出到终端 stdout */
        exec_cmd_in_child(args, argc, STDIN_FILENO, STDOUT_FILENO);
    }
    /* 父进程：等待子进程结束 */
    waitpid(pid, NULL, 0);
}

static void run_pipeline(char *segments[], int nseg) {
    int npipes = nseg - 1;

    /* --- 1. 一次性创建所有管道 --- */
    int pipes[MAX_PIPES][2];
    for (int i = 0; i < npipes; i++) {
        if (pipe(pipes[i]) < 0) {
            perror("pipe");
            /* 出错：关闭已创建的管道后返回 */
            for (int j = 0; j < i; j++) {
                close(pipes[j][0]);
                close(pipes[j][1]);
            }
            return;
        }
    }

    /* --- 2. 依次 fork 每段命令的子进程 --- */
    pid_t pids[MAX_PIPES + 1];

    for (int i = 0; i < nseg; i++) {

        /* 确定本段从哪里读、往哪里写：
         *   第一段：从终端 stdin 读
         *   中间段：从上一个管道的读端读，向下一个管道的写端写
         *   最后段：向终端 stdout 写                              */
        int in_fd  = (i == 0)        ? STDIN_FILENO  : pipes[i - 1][0];
        int out_fd = (i == nseg - 1) ? STDOUT_FILENO : pipes[i][1];

        /* 解析本段命令的参数 */
        char  cbuf[MAX_CMD];
        strncpy(cbuf, segments[i], MAX_CMD - 1);
        cbuf[MAX_CMD - 1] = '\0';
        char *args[MAX_ARGS];
        int   argc = parse_args(cbuf, args);

        pid_t pid = fork();
        if (pid < 0) {
            perror("fork");
            /* fork 失败：关闭所有管道，等待已启动的子进程 */
            for (int j = 0; j < npipes; j++) {
                close(pipes[j][0]);
                close(pipes[j][1]);
            }
            for (int j = 0; j < i; j++) waitpid(pids[j], NULL, 0);
            return;
        }

        if (pid == 0) {
            /* 子进程：
             *   关闭所有"不是自己用"的管道端，
             *   防止 EOF 永远不到达读端。         */
            for (int j = 0; j < npipes; j++) {
                if (pipes[j][0] != in_fd)  
                    close(pipes[j][0]);
                if (pipes[j][1] != out_fd) 
                    close(pipes[j][1]);
            }
            if (argc == 0) 
                exit(0);
            exec_cmd_in_child(args, argc, in_fd, out_fd);
            /* exec_cmd_in_child 内部会 exit，永远不会走到这里 */
        }

        pids[i] = pid;   /* 父进程记录子进程 PID */
    }

    /* --- 3. 父进程关闭所有管道端，再统一等待子进程 ---
     *   必须先全部关闭，否则最后一段命令永远读不到 EOF。 */
    for (int i = 0; i < npipes; i++) {
        close(pipes[i][0]);
        close(pipes[i][1]);
    }
    for (int i = 0; i < nseg; i++) {
        waitpid(pids[i], NULL, 0);
    }
}

void execute(char *cmdline) {
    char  buf[MAX_CMD];
    strncpy(buf, cmdline, MAX_CMD - 1);
    buf[MAX_CMD - 1] = '\0';

    char *segments[MAX_PIPES + 1];
    int   nseg = split_pipeline(buf, segments, MAX_PIPES);

    if (nseg <= 0) return;          /* 空命令或出错 */

    if (nseg == 1) {
        run_single(segments[0]);    /* 普通命令，无管道 */
    } else {
        run_pipeline(segments, nseg); /* 多段管道命令 */
    }
}

int main() {
    char cmdline[MAX_CMD];

    while (1) {
        printf("%% ");
        fflush(stdout);

        if (fgets(cmdline, MAX_CMD, stdin) == NULL) {
            printf("\n");
            break;
        }

        char *cmd = trim(cmdline);
        if (strlen(cmd) == 0) continue;

        if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "logout") == 0) {
            printf("Goodbye!\n");
            break;
        }

        execute(cmd);
    }
    return 0;
}