#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <ncurses.h>
#include <stdlib.h>
#include <panel.h>

#include "mainlib.h"
#include "keylib.h"

extern int screen_size_cols;
extern int screen_size_lines;
extern char original_path[STRING_MAX];
extern char target_path[STRING_MAX];
extern char current_path[STRING_MAX];

extern WINDOW 	*w_dirtree;
extern PANEL 	*p_dirtree;

extern int write_log(char *);
extern int exit_program(void);

//dirctory struct
typedef struct directory
{
        int x;  //locate x
        int y;  //locate y
        char path[1024];        // full dirctory path
        char showPath[256];
        int hasSubDir; 
        int subDirEa;
        struct directory *parent;
        struct directory *child;
        struct directory *next;
        struct directory *next_r;
}DIR_NODE;
 
DIR_NODE *root;
DIR_NODE *cur_p;

int size_dirtree_lines  ;
int size_dirtree_cols   ;

int dirsearch(DIR *dir, char *file);
int dirtree_new();
int dirtree_show();
int checkHasDir(char* path);
int resetPosition(DIR_NODE *node, int count);
int drowDirTree(WINDOW* w_dirtree, DIR_NODE *node);
int extendSubDir(DIR_NODE *parent);
int reduceSubDir(DIR_NODE *parent);
int removeDirTree(DIR_NODE *parent);
int messageBox(char* buff, int flag);
int setStartPosition();
int drawTitleBar(WINDOW *w_dirtree); 
int windowErase(WINDOW *w_dirtree);

/*int main(void)
{
        WINDOW *w_dirtree;
        char path[1024];
 
        initscr();
        //cbreak();
        start_color();
 
        keypad(stdscr, TRUE);
        //noecho();
 
        w_dirtree = newwin(LINES, COLS, 0, 0);
 
        startDirTree(w_dirtree, path);
 
        endwin();
 
        printf("\nresult : %s", path);
 
        return 0;
}*/
 

int dirtree_new()
{
	size_dirtree_lines	= screen_size_lines-20;
	size_dirtree_cols	= screen_size_cols-40;

	w_dirtree = newwin(size_dirtree_lines, size_dirtree_cols, 8, 20);
	if(w_dirtree == NULL)
	{
		write_log("Error dirtree_new() / malloc\n");
		return -1;
	}

	p_dirtree = new_panel(w_dirtree);
	if(p_dirtree == NULL)
	{
		write_log("Error dirtree_new() / new_panel\n");
		return -1;
	}

	hide_panel(p_dirtree);

	return 0;
}



int dirtree_show()
{

	DIR_NODE *temp;
	int ch;
	char buff_1[1024];
	char buff_2[512];
	
	show_panel(p_dirtree);
	update_panels();
	doupdate();

	root = (DIR_NODE*)malloc(sizeof(DIR_NODE));
	if(root == NULL)
	{
		write_log("Error dirtree_show() / malloc\n");
		exit_program();
	}
	root->x = START_COLS;
	root->y = START_ROWS;
	root->subDirEa = 0;
	strcpy(root->path, "/");
	strcpy(root->showPath, "/");
	root->hasSubDir = checkHasDir("/");
	root->parent = NULL;
	root->child = NULL;
	root->next = NULL;
	root->next_r= NULL;

	//extendSubDir(root);

	//cur_p = root->child;
	cur_p = root;
	
	if(setStartPosition() == -1)
	{
		write_log("Error setStartPosition()\n");
		exit_program();
	}
 
	if(drowDirTree(w_dirtree, root) == -1)
	{
		write_log("Error drowDirTree()\n");
		exit_program();
	}

	if(drawTitleBar(w_dirtree) == -1)
	{
		write_log("Error drawTitleBar()\n");
		exit_program();
	}

	box(w_dirtree, 0, 0);
	wrefresh(w_dirtree);

    while((ch = getch()) != KEY_F(1))
    {

		switch(ch)
		{
		case KEY_UP:
			if(cur_p->next_r != NULL)
			{
				//werase(w_dirtree);
				windowErase(w_dirtree);
				cur_p = cur_p->next_r;
                if( drowDirTree(w_dirtree, root) == -1)
				{
					write_log("Error drowDirTree()\n");
					exit_program();
				}
				drawTitleBar(w_dirtree);
				box(w_dirtree, 0, 0);
				wrefresh(w_dirtree);
			}
			break;

		case KEY_DOWN:
			if(cur_p->next != NULL)
			{
				//werase(w_dirtree);
				windowErase(w_dirtree);
				cur_p = cur_p->next;
				if( drowDirTree(w_dirtree, root) == -1)
				{
					write_log("Error drowDirTree()\n");
					exit_program();
				}
				drawTitleBar(w_dirtree);
				box(w_dirtree, 0, 0);
				wrefresh(w_dirtree);
			}
			break;

		case KEY_RIGHT:
			if(cur_p->hasSubDir == 1)
			{
				//werase(w_dirtree);
				windowErase(w_dirtree);
				if( extendSubDir(cur_p) == -1)
				{
					write_log("Error drowDirTree()\n");
					exit_program();
				}
				cur_p = cur_p->child;
				if( drowDirTree(w_dirtree, root) == -1)
				{
					write_log("Error drowDirTree()\n");
					exit_program();
				}
				drawTitleBar(w_dirtree);
				box(w_dirtree, 0, 0);
				wrefresh(w_dirtree);
			}
			break;
 
		case KEY_LEFT:
			if(cur_p != root)
			{
				//werase(w_dirtree);
				windowErase(w_dirtree);
				temp = cur_p->parent;
				if( reduceSubDir(cur_p->parent) == -1)
				{
					write_log("Error reduceSubDir()\n");
					exit_program();
				}
				cur_p = temp;
				if( drowDirTree(w_dirtree, root) == -1)
				{
					write_log("Error drowDirTree()\n");
					exit_program();
				}
				drawTitleBar(w_dirtree);
				box(w_dirtree, 0, 0);
				wrefresh(w_dirtree);
			}
			break;

			case KEY_USER_ENTER:
				strcpy(target_path, cur_p->path);
				if( removeDirTree(root) == -1)
				{
					write_log("Error removeDirTree()\n");
					exit_program();
				}
				wclear(w_dirtree);
				wrefresh(w_dirtree);
				hide_panel(p_dirtree);
				update_panels();
				doupdate();
				//delwin()
				//del_panel();
				return ;
				break;
         

		/*case KEY_F(2):	
			//if(messageBox(buff_2, MKDIR) == 1)
			//{
				
				sprintf(buff_1, "mkdir %s/test", cur_p->path);//, buff_2);
				system(buff_1);
				sprintf(buff_1, "%s/test", cur_p->path);
				extendSubDir(cur_p);

			
				for(temp = cur_p->child; temp != NULL; temp = temp->next)
					if(strcmp(temp->path, buff_1) == 0)
						cur_p = temp;

				drowDirTree(w_dirtree, root);
			//}
			break;*/

		/*case KEY_ESC:
			removeDirTree(root);
			wclear(w_dirtree);
			hide_panel(p_dirtree);
			update_panels();
			doupdate();
			delwin(w_dirtree);
			return 0;
			break;*/

		}//end switch
	}// end while
 
	wclear(w_dirtree);
	return ;
}
 


int drowDirTree(WINDOW *w_dirtree, DIR_NODE *node)
{
	int y = 0, x = 0;
	int scroll = 0;
	DIR_NODE *temp;
 
	if(node == NULL)
	{
		write_log("Error drowDirTree() / node is NULL!!\n");
		return -1;
	}

	getmaxyx(w_dirtree, y, x);

	for(temp = node; temp != NULL; temp = temp->next)
	{

		//scroll operation!!
		scroll = cur_p->y - (y-2) ;
		if( scroll < 0 ) scroll = 0;


		if( (temp->y - scroll) <= y && (temp->y - scroll) >= START_ROWS )
		{
			if(temp->showPath == NULL)
			{
				write_log("Error drowDirTree() / temp->showPath is NULL!!\n");
				return -1;
			}

			if(temp == cur_p)
			{
				wattron(w_dirtree, COLOR_PAIR(7));
				if(temp->hasSubDir == 1)
				{
					mvwprintw(w_dirtree, temp->y - scroll, temp->x, "+%s", temp->showPath);
				}
				else
				{
					mvwprintw(w_dirtree, temp->y - scroll, temp->x, "%s", temp->showPath);
				}
				wattroff(w_dirtree,COLOR_PAIR(7));
			}
			else
			{
				if(temp->hasSubDir == 1)
				{
					wattron(w_dirtree, COLOR_PAIR(1));
					mvwprintw(w_dirtree, temp->y - scroll, temp->x, "+%s", temp->showPath);
					wattroff(w_dirtree, COLOR_PAIR(1));
				}	
				else
				{
					mvwprintw(w_dirtree, temp->y - scroll, temp->x, "%s", temp->showPath);
				}
			}
		}

    
		if( temp->child != NULL)
		{
			if( drowDirTree(w_dirtree, temp->child) == -1)
				return -1;
		}

	}//end for
 
	return 0;
}



int drawTitleBar(WINDOW *w_dirtree)
{
	mvwhline(w_dirtree, 2, 1, ACS_HLINE, size_dirtree_cols);
	wattron(w_dirtree, COLOR_PAIR(1));
	mvwprintw(w_dirtree, 1, (size_dirtree_cols/2)-(strlen("Dir Tree Viewer")/2), "Dir Tree Viewer");
	wattroff(w_dirtree, COLOR_PAIR(1));

	return 0;
}



int windowErase(WINDOW *w_dirtree)
{
	int i=0, j=0;
	int y=0, x=0;


	getmaxyx(w_dirtree, y, x);

	for(i=0; i<y; i++)
		for(j=0; j<x; j++)
			mvwprintw(w_dirtree, i ,j ," ");

	return 0;

}



int extendSubDir(DIR_NODE *parent)
{
    //find subDir form parent node
    DIR *dir;
    struct dirent *ep;
    struct stat st;
    char buff[1024];
    DIR_NODE *temp;

    int count = 0; //subDir counter

	if(parent == NULL)
	{
		write_log("Error extendSubDir()/parent is NULL!!\n");
		return -1;
	}
    dir = opendir(parent->path);

    if(dir != NULL)
    {
	while(ep = readdir(dir))
	{
            if(!strcmp(parent->path, "/"))
		sprintf(buff, "/%s", ep->d_name);		
            else
		sprintf(buff, "%s/%s", parent->path, ep->d_name);

            stat(buff, &st);

            // "." and ".." are net show
            if( strcmp(ep->d_name,".") == 0 || strcmp(ep->d_name, "..") == 0 ) 
				continue;

            // Select Directory file. Except link.
            if( S_ISDIR(st.st_mode) && !(S_ISLNK(st.st_mode)) )
            {
                //printf("\n%s", buff);

                count++;

                //Create Dir node
                if(parent->child == NULL)
                {
					parent->child = (DIR_NODE*)malloc(sizeof(DIR_NODE));
					if(parent->child == NULL)
					{
						write_log("Error extendSubDir()/malloc\n");
						return -1;
					}
					strcpy(parent->child->path, buff);
                    strcpy(parent->child->showPath, ep->d_name);
                    parent->child->hasSubDir = checkHasDir(buff);
                    parent->child->subDirEa = 0;
                    parent->child->x = parent->x + COL_OFFSET;
                    parent->child->y = parent->y + 1;
                    parent->child->parent = parent;
                    parent->child->child = NULL;
                    parent->child->next = NULL;
                    parent->child->next_r = NULL;
                    temp = parent->child;
                }
                else
                {
                    temp->next = (DIR_NODE*)malloc(sizeof(DIR_NODE));
					if(temp->next == NULL)
					{
						write_log("Error extendSubDir()/malloc\n");
						return -1;
					}
                    temp->next->next_r = temp;
                    //memcpy(temp->next_r, temp, sizeof(DIR_NODE*));

                    strcpy(temp->next->path,buff);
                    strcpy(temp->next->showPath, ep->d_name);
                    temp->next->hasSubDir = checkHasDir(buff);
                    temp->next->subDirEa = 0;
                    temp->next->x = temp->x ;
                    temp->next->y = temp->y + 1;
                    temp->next->parent = parent;
                    temp->next->child = NULL;
                    temp->next->next = NULL;
                    temp = temp->next;
                }
            }//end if
        }//end while

        parent->subDirEa = count;
        if( resetPosition(parent, count) == -1)
		{
			write_log("Error extendSubDir()/resetPosition()\n");
			return -1;
		}

        (void)closedir(dir);
        return 0;
    }
    else
	{
        (void)closedir(dir);
        write_log("Error extendSubDir()/dir is NULL!!\n");
		return -1;
	}
}




int setStartPosition()
{
	char *temp;
	DIR_NODE *tempNode;
	int n = 0;
	int i = 0;

	char tempDir[255] = {0};
	strcpy(tempDir, current_path);

	// get Directory depth
	temp = strtok(current_path, "/");
	if(temp != NULL)
		n++;
	else return 0;

	if( extendSubDir(root) == -1)
	{
		write_log("Error setStartPosition()/extendSubDir()\n");
		return -1;
	}
	cur_p = root;

	while(temp != NULL)
	{
		temp = strtok(NULL, "/");
		if(temp) n++;
	}

	strcpy(current_path, tempDir);

	// extend tree
	temp = strtok(tempDir,"/");
	if(temp != NULL)
	{
		for(tempNode = cur_p->child; tempNode != NULL; tempNode = tempNode->next)
		{
			if(strcmp(tempNode->showPath, temp) == 0)
			{
				cur_p = tempNode;
				if( extendSubDir(cur_p) == -1 )
				{
					write_log("Error setStartPosition()/extendSubDir()\n");
					return -1;
				}
			}
		}
	}

	
	for(i=0; i < n-2; i++)
	{
		temp = strtok(NULL, "/");
		if(temp)
		{
			for(tempNode = cur_p->child; tempNode != NULL; tempNode = tempNode->next)
			{
		        if(strcmp(tempNode->showPath, temp) == 0)
				{
					cur_p = tempNode;
					if( extendSubDir(cur_p) == -1)
					{
						write_log("Error setStartPosition()/extendSubDir()\n");
						return -1;
					}
				}
			}
		}
	}

	//set cur_p
	temp = strtok(NULL, "/");
	if(temp)
	{
		for(tempNode = cur_p->child; tempNode != NULL; tempNode = tempNode->next)
			if(strcmp(tempNode->showPath, temp) == 0)
				cur_p = tempNode;
	}
	return 0;
}


 
int reduceSubDir(DIR_NODE *parent)
{
    DIR_NODE *temp;

	if(parent == NULL)
	{
		write_log("Error reduceSubDir()/parent is NULL!!\n");
		return -1;
	}
    temp = parent->child->next;

    //case : subDir is one
    if(temp == NULL)
    {
            free((DIR_NODE*)parent->child);
    }
    //case : subDir are one more
    else
    {
        while(temp != NULL)
        {
            free((DIR_NODE*)temp->next_r);
            //final node case
            if(temp->next == NULL) 
                    free((DIR_NODE*)temp);

            temp = temp->next;
	}
    }

    parent->child = NULL;

    // set position
    if( resetPosition(parent, -(parent->subDirEa)) == -1)
	{
		write_log("Error reduceSubDir()/presetPosition()\n");
		return -1;
	}

    parent->subDirEa = 0;

    return 0;
}
 

int checkHasDir(char* path)
{
    DIR *dir;
    struct dirent *ep;
    struct stat st;
    char buff[1024];

    dir = opendir(path);

    if(dir != NULL)
    {
        while(ep = readdir(dir))
        {
            sprintf(buff, "%s/%s", path, ep->d_name);
            stat(buff, &st);

            if( strcmp(ep->d_name,".") == 0 || strcmp(ep->d_name, "..") == 0 )
				continue;

            if( S_ISDIR(st.st_mode) && !(S_ISLNK(st.st_mode)) )
				return 1;
        }
    }
    (void)closedir(dir);
    return 0;
}
 
 

int resetPosition(DIR_NODE *node, int count)
{
    DIR_NODE *temp;

	if(node == NULL)
	{
		write_log("Error resetPosition()/node is NULL!!\n");
		return -1;
	}

    if(node == root) return 0;

    for(temp = node->next; temp != NULL; temp = temp->next)
    {
		temp->y = temp->y + count;
    }

    if( resetPosition(node->parent, count) == -1 )
	{
		return -1;
	}

    return 0;
}
 


int removeDirTree(DIR_NODE* parent)
{
	DIR_NODE *temp;
 
	if( parent == NULL )
	{
		write_log("Error removeDirTree()/parent is NULL!!\n");
		return -1;
	}

	if(parent == root) 
	{
		free((DIR_NODE*)parent);
		return 0;
	}

    temp = parent->child->next;

    //case : subDir is one
    if(temp == NULL)
    {
        //if has suddir -> recursive
        if(parent->child->child != NULL)
			if(removeDirTree(parent->child) == -1)
				return -1;

        free((DIR_NODE*)parent->child);
    }
    else //case : subDir are one more
    {
        while(temp != NULL)
        {
            if(temp->next_r->child != NULL)
				if( removeDirTree(temp->next_r) == -1)
					return -1;

            free((DIR_NODE*)temp->next_r);

            //final node case
            if(temp->next == NULL)
            {
                if(temp->child != NULL)
					if( removeDirTree(temp) == -1)
						return -1;

                free((DIR_NODE*)temp);
            }

            temp = temp->next;
        }
    }

    return 0;
}


//각정 정보 표기 및 사용자 입력을 받는 메시지 박스
/*int messageBox(char* buff, int flag)
{
	WINDOW *mes_win;

	newwin(MSG_WIN_SIZE_ROWS, MSG_WIN_SIZE_COLS, (LINES/2) - (MSG_WIN_SIZE_ROWS/2), (COLS/2) - (MSG_WIN_SIZE_COLS/2));
}*/
