/* Copyright 2013 Akira Ohta (akohta001@gmail.com)
    This file is part of ntch.

    The ntch is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    The ntch is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ntch.  If not, see <http://www.gnu.org/licenses/>.
    
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ncursesw/ncurses.h>
#include <locale.h>
#include <iconv.h>
#include <netinet/in.h>
#include <assert.h>

#include "env.h"
#include "error.h"
#include "nt_string.h"
#include "utils/nt_std_t.h"
#include "net/nt_http.h"
#include "net/nt_socket.h"
#include "net/nt_cookie.h"
#include "utils/file.h"
#include "utils/nt_usr_db.h"
#include "utils/nt_mutex.h"
#include "usr/favorite_t.h"
#include "_2ch/_2ch.h"
#include "_2ch/maru_2ch.h"
#include "_2ch/search_2ch.h"
#include "ui/disp.h"
#include "ui/disp_win.h"
#include "ui/disp_string.h"

#define S_SIZE 	(1024)

static int draw_title(WINDOW *wp, wchar_t *title, attr_t attr);
static BOOL DoLoop(WINDOW *scrp, void *db_handle,
			nt_favorite_handle h_favorite, 
			nt_favorite_grp_handle h_favorite_grp);
static void print_error(WINDOW *wp, const wchar_t *msg);
static BOOL set_sel_item(nt_2ch_model_tp modelp,
			nt_2ch_selected_item_tp sel_itemp, 
			nt_searched_thread_tp sel_threadp, const wchar_t** errorpp);


int main(int argc, char *argv[])
{
	int result = 1;
	WINDOW *scrp;
	void *usr_db_handle;
	nt_favorite_handle h_favorite;
	nt_favorite_grp_handle h_favorite_grp;
	nt_link_tp text_linkp, text2_linkp;

	if(0 != set_option(argc, argv)){
		return 1;
	}


	sleep(1);

	setlocale(LC_ALL, "ja_JP.UTF-8");

	usr_db_handle = nt_usr_db_init_lib(USR_LOG_DB_PATH);
	if(!usr_db_handle){
		fputs("Couldn't initialize usr database.\n", stderr);
		printf("Exit.");
		return 1;
	}

	scrp = initscr();
	if(!scrp){
		fputs("Couldn't initialize Curses libraly.\n", stderr);
		printf("Exit.");
		return 1;
	}

	if(!nt_2ch_model_init()){
		fputs("Failed to read board menu data.\n", stderr);
		goto ERROR_TRAP;
	}

	if(!nt_init_board_menu()){
		fputs("Failed to initialize board menu data.\n", stderr);
		goto ERROR_TRAP;
	}
	
	nt_mutex_lib_init();

	h_favorite = nt_favorite_alloc(L"favorite");
	if(!h_favorite){
		return FALSE;
	}
	h_favorite_grp = nt_favorite_grp_alloc(h_favorite, 
				L"お気に入りのスレッド",
				NT_FAVORITE_GRP_FLAG_FOLDER_OPEN);
	if(!h_favorite_grp){
		nt_favorite_release_ref(h_favorite);
		return FALSE;
	}
	
	text_linkp = nt_read_text_file(USR_FAVORITE_BOARD_FILE_PATH);
	if(text_linkp){
		nt_favorite_load_boards(h_favorite, text_linkp);
		nt_all_link_free(text_linkp, free);
	}
	text_linkp = nt_read_text_file(USR_FAVORITE_THREAD_FILE_PATH);
	if(text_linkp){
		nt_favorite_load_threads(h_favorite, h_favorite_grp, text_linkp);
		nt_all_link_free(text_linkp, free);
	}
	
	cbreak();
	noecho();

	if(DoLoop(scrp, usr_db_handle, h_favorite, h_favorite_grp))
		result = 0;

	echo();
	nocbreak();
	
	text_linkp = nt_favorite_retrieve_boards(h_favorite);
	if(text_linkp){
		nt_write_text_file(USR_FAVORITE_BOARD_FILE_PATH, text_linkp);
		nt_all_link_free(text_linkp, free);
	}else{
		unlink(USR_FAVORITE_BOARD_FILE_PATH);
	}
	if(nt_favorite_retrieve_threads(h_favorite, 
			&text_linkp, &text2_linkp)){
		if(text_linkp)
			nt_all_link_free(text_linkp, free);
		if(text2_linkp){
			nt_write_text_file(USR_FAVORITE_THREAD_FILE_PATH, text2_linkp);
			nt_all_link_free(text2_linkp, free);
		}else{
			unlink(USR_FAVORITE_THREAD_FILE_PATH);
		}
	}

	nt_favorite_grp_release_ref(h_favorite_grp);
	nt_favorite_release_ref(h_favorite);

ERROR_TRAP:
	clear();


	nt_2ch_model_free(app_2ch_modelp);

	endwin();

	nt_usr_db_finish_lib(usr_db_handle);
	
	nt_mutex_lib_finish();

	return (result);
}


static BOOL DoLoop(WINDOW *scrp, void *db_handle,
		nt_favorite_handle h_favorite, 
		nt_favorite_grp_handle h_favorite_grp)
{
	int ch, state, num;
	int disp_state, nresult;
	nt_window_tp bwinp = NULL;
	nt_window_tp twinp = NULL;
	nt_window_tp rwinp = NULL;
	nt_window_tp search_winp = NULL;
	nt_window_tp favorite_winp = NULL;
	char buf[256];
	wchar_t wch, *title;
	const wchar_t *status_msg = NULL;
	BOOL result = FALSE;
	nt_write_data_tp writep = NULL;
	nt_maru_2ch_tp marup = NULL;
	nt_cookie_tp cookiep;
	nt_link_tp linkp;
	char *search;
	nt_searched_thread_tp sel_threadp;
	nt_2ch_selected_item_t sel_items;
	nt_2ch_selected_item_t sel_items_tmp;
	nt_favorite_board_handle h_favorite_board;
	nt_favorite_thread_handle h_favorite_thread;
	void *handle;
	
	disp_state = DISP_STATE_BOARDMENU; 
	cookiep = nt_load_cookies(USR_COOKIE_PATH);

	if(MARU_ID && MARU_PW) 
		marup = nt_maru_2ch_alloc(MARU_ID, MARU_PW);

	ch = NT_KEY_NONE;
	bwinp = nt_disp_win_alloc(scrp, LINES-1, COLS, 1, 0, buf);
	if(!bwinp)
		return FALSE;
	twinp = nt_disp_win_alloc(scrp, LINES-1, COLS, 1, 0, buf);
	if(!twinp)
		return FALSE;
	rwinp = nt_disp_win_alloc(scrp, LINES-1, COLS, 1, 0, buf);
	if(!rwinp)
		return FALSE;
	
	keypad(stdscr, true);
	while(1){
		if(FORCE_REFRESH)
			wclear(scrp);
		else
			werase(scrp);
		switch(disp_state){
		case DISP_STATE_BOARDMENU:
			werase(bwinp->wp);
			bwinp->key = ch;
			draw_title(scrp, L"板一覧", WA_REVERSE);
			if(!nt_disp_win_move(scrp, bwinp, LINES-1, COLS, 1, 0))
				goto END_WHILE;
			state = disp_board_menu(bwinp, app_2ch_modelp, &sel_items);
			if(DISP_CMD(state)){
				if(DISP_CMD(state) == DISP_CMD_ADD_FAVORITE){
					h_favorite_board = nt_favorite_board_alloc(
						h_favorite,
						sel_items.selected_boardp->name);
					if(h_favorite_board){
						nt_favorite_board_release_ref(h_favorite_board);
						//favorite_dump(h_favorite);
					}
				}
				state = DISP_STATE_BOARDMENU;
				ch = NT_KEY_NONE;
			}
			if(DISP_STATE_ERROR == state){
				goto END_WHILE;
			}else if(DISP_STATE_THREADTITLE == state){
				if(nt_read_board(&sel_items)){
					disp_state = state;
					if(twinp->data){
						free_threadlist_ctx(twinp->data);
						twinp->data = NULL;
					}
					ch = NT_KEY_NONE;
					continue;
				}else{
					print_error(scrp, NT_ERR_MSG_COUDLNOT_READ_BOARD);
				}
			}else if(state == DISP_STATE_SEARCH_THREAD){
				state = DISP_STATE_BOARDMENU;
				disp_state = DISP_STATE_SEARCH_THREAD;
				ch = NT_KEY_NONE;
				continue;
			}else if(DISP_STATE_FAVORITE == state){
				state = DISP_STATE_BOARDMENU;
				disp_state = DISP_STATE_FAVORITE;
				ch = NT_KEY_NONE;
				continue;
			}
			break;
		case DISP_STATE_THREADTITLE:
			werase(twinp->wp);
			twinp->key = ch;
			twinp->status_msg = NULL;
			title = sel_items.selected_boardp->name;
			draw_title(scrp, title, WA_REVERSE);
			if(!nt_disp_win_move(scrp, twinp, LINES-1, COLS, 1, 0))
				goto END_WHILE;
			state = disp_threadlist(twinp, &sel_items, db_handle);
			if(DISP_CMD(state)){
				if(DISP_CMD(state) == DISP_CMD_ADD_FAVORITE){
					h_favorite_thread = nt_favorite_thread_alloc(
						h_favorite_grp,
						sel_items.selected_threadp->file_name,
						sel_items.selected_boardp->name,
						sel_items.selected_threadp->name);
					if(h_favorite_thread){
						nt_favorite_thread_release_ref(h_favorite_thread);
						//favorite_dump(h_favorite);
						status_msg = NT_INFO_ADD_FAVORITE_SUCCEEDED;
					}else{
						status_msg = NT_ERR_MSG_ADD_FAVORITE_FAILED;
					}
				}
				state = DISP_STATE_THREADTITLE;
				ch = NT_KEY_NONE;
			}else if(DISP_STATE_ERROR == state){
				goto END_WHILE;
			}else if(DISP_STATE_BOARDMENU == state){
					disp_state = state;
					ch = NT_KEY_NONE;
					continue;
			}else if(DISP_STATE_REFRESH == state){
					if(nt_read_board(&sel_items)){
						if(twinp->data){
							free_threadlist_ctx(twinp->data);
							twinp->data = NULL;
						}
						ch = NT_KEY_NONE;
						status_msg = NT_INFO_REFRESH_BOARD_SUCCESS;
						continue;
					}else{
						status_msg = NT_ERR_MSG_COUDLNOT_READ_BOARD;
					}
			}else if(DISP_STATE_RESLIST == state){
					if(!nt_read_thread(&sel_items)){
						wclear(scrp);
						ch = NT_KEY_NONE;
						continue;
					}
					if(twinp->data){
						init_threadlist_ctx(twinp->data);
					}
					if(rwinp->data){
						free_reslist_ctx(rwinp->data);
						rwinp->data = NULL;
					}
					disp_state = state;
					ch = NT_KEY_NONE;
					continue;
			}else if(DISP_STATE_SEARCH_THREAD == state){
				state = DISP_STATE_THREADTITLE;
				disp_state = DISP_STATE_SEARCH_THREAD;
				ch = NT_KEY_NONE;
				continue;
			}else if(DISP_STATE_FAVORITE == state){
				state = DISP_STATE_THREADTITLE;
				disp_state = DISP_STATE_FAVORITE;
				ch = NT_KEY_NONE;
				continue;
			}else{
				status_msg = twinp->status_msg;
			}
			break;
		case DISP_STATE_RESLIST:
			werase(rwinp->wp);
			title = sel_items.selected_threadp->name;
			num = draw_title(scrp, title, WA_REVERSE);
			if(!nt_disp_win_move(scrp, rwinp, 
						LINES - num, COLS, num, 0))
				goto END_WHILE;
			rwinp->key = ch;
			state = disp_reslist(rwinp, &sel_items, db_handle);
			if(DISP_STATE_ERROR == state){
				goto END_WHILE;
			}else if(state == DISP_STATE_REFRESH){
					ch = NT_KEY_NONE;
					if(!nt_read_thread(&sel_items)){
						status_msg = NT_ERR_MSG_REFRESH_THREAD_FAILED;
						wclear(scrp);
						continue;
					}
					if(rwinp->data){
						free_reslist_ctx(rwinp->data);
						rwinp->data = NULL;
					}
					status_msg = NT_INFO_REFRESH_THREAD_SUCCESS;
					wclear(scrp);
					continue;
			}else if(state == DISP_STATE_SEARCH_THREAD){
				state = DISP_STATE_RESLIST;
				disp_state = DISP_STATE_SEARCH_THREAD;
				ch = NT_KEY_NONE;
				continue;
			}else if(DISP_STATE_FAVORITE == state){
				state = DISP_STATE_RESLIST;
				disp_state = DISP_STATE_FAVORITE;
				ch = NT_KEY_NONE;
				continue;
			}else if(state != DISP_STATE_RESLIST){
				disp_state = state;
				ch = NT_KEY_NONE;
				continue;
			}
			break;
		case DISP_STATE_EDITOR:
			disp_state = DISP_STATE_RESLIST;
			ch = NT_KEY_NONE;
			if(writep)
				nt_write_data_free(writep);
			writep = nt_write_data_alloc();
			if(!writep)
				goto END_WHILE;
			if(disp_editor(writep)){
				if(marup && !marup->sid)
					get_session_id(marup);
				if(nt_write_msg(&sel_items, 
						writep, cookiep, marup)){
					disp_state = DISP_STATE_HTML_RESULT;
					continue;
				}
			}
			status_msg = writep->status_msg;
			wclear(scrp);
			continue;
		case DISP_STATE_HTML_RESULT:
			nresult = disp_html_result(writep);
			if(0 == nresult){
				if(!nt_read_thread(&sel_items))
					break;
				if(rwinp->data){
					free_reslist_ctx(rwinp->data);
					rwinp->data = NULL;
				}
			}else if(1 == nresult){
				if(nt_write_msg(&sel_items, 
						writep, cookiep, marup)){
					if(0 == disp_html_result(writep)){
						if(!nt_read_thread(&sel_items))
							break;
						if(rwinp->data){
							free_reslist_ctx(rwinp->data);
							rwinp->data = NULL;
						}
					}
				}
			}
			disp_state = DISP_STATE_RESLIST;
			ch = NT_KEY_NONE;
			status_msg = writep->status_msg;
			wclear(scrp);
			continue;
		case DISP_STATE_SEARCH_THREAD:
			draw_title(scrp, L"全板検索", WA_REVERSE);
			if(!search_winp)
				search_winp = nt_disp_win_alloc(
						scrp, LINES-1, COLS, 1, 0, buf);
			if(!search_winp)
				goto END_WHILE;
			search_winp->key = ch;
			linkp = NULL;
			if(nt_get_search_text(buf, &search)){
				if(search){
					linkp = nt_search_all_board(
							app_2ch_modelp, search, &status_msg);
					free(search);
					buf[0] = '\0';
				}
			}
			disp_state = disp_thread_search(search_winp, 
					state, linkp, &sel_threadp);
			ch = NT_KEY_NONE;
			if(sel_threadp){
				if(set_sel_item(app_2ch_modelp,
						&sel_items_tmp, sel_threadp, &status_msg)){
					disp_state = DISP_STATE_RESLIST;
					sel_items = sel_items_tmp;
					if(rwinp->data){
						free_reslist_ctx(rwinp->data);
						rwinp->data = NULL;
					}
					if(twinp->data){
						free_threadlist_ctx(twinp->data);
						twinp->data = NULL;
					}
				}
				continue;
			}
			if(disp_state != DISP_STATE_SEARCH_THREAD){
				continue;
			}
			break;
		case DISP_STATE_FAVORITE:
			draw_title(scrp, L"お気に入り", WA_REVERSE);
			if(!favorite_winp)
				favorite_winp = nt_disp_win_alloc(
						scrp, LINES-1, COLS, 1, 0, buf);
			if(!favorite_winp)
				goto END_WHILE;
			favorite_winp->key = ch;
			state = disp_favorite(favorite_winp, state, h_favorite, 
							&handle,  &sel_threadp);
			if(DISP_CMD(state)){
				ch = NT_KEY_NONE;
				if(DISP_CMD(state) == DISP_CMD_DEL_FAVORITE_BOARD){
					if(nt_favorite_board_remove(handle)){
						h_favorite_board = (nt_favorite_board_handle)handle;
						nt_favorite_board_release_ref(h_favorite_board);
						status_msg = NT_INFO_DEL_FAVORITE_SUCCEEDED;
					}else{
						status_msg = NT_ERR_MSG_DEL_FAVORITE_FAILED;
					}
				}else if(DISP_CMD(state) == DISP_CMD_DEL_FAVORITE_THREAD){
					if(nt_favorite_thread_remove(handle)){
						h_favorite_thread = (nt_favorite_thread_handle)handle;
						nt_favorite_thread_release_ref(h_favorite_thread);
						status_msg = NT_INFO_DEL_FAVORITE_SUCCEEDED;
					}else{
						status_msg = NT_ERR_MSG_DEL_FAVORITE_FAILED;
					}
				}else if(DISP_CMD(state) == DISP_CMD_SEL_FAVORITE_BOARD ||
						DISP_CMD(state) == DISP_CMD_SEL_FAVORITE_THREAD){
					if(set_sel_item(app_2ch_modelp,
							&sel_items_tmp, sel_threadp, &status_msg)){
						disp_state = (DISP_CMD(state) == DISP_CMD_SEL_FAVORITE_BOARD)
							? DISP_STATE_THREADTITLE : DISP_STATE_RESLIST;
						sel_items = sel_items_tmp;
						if(rwinp->data){
							free_reslist_ctx(rwinp->data);
							rwinp->data = NULL;
						}
						if(twinp->data){
							free_threadlist_ctx(twinp->data);
							twinp->data = NULL;
						}
					}
					if(sel_threadp->board_name)
						free(sel_threadp->board_name);
					if(sel_threadp->dat_name)
						free(sel_threadp->dat_name);
					free(sel_threadp);
				}else{
					disp_state = state = DISP_STATE_FAVORITE;
				}
				continue;
			}else if(state != DISP_STATE_FAVORITE){
				ch = NT_KEY_NONE;
				disp_state = state;
				continue;
			}else{
				disp_state = DISP_STATE_FAVORITE;
			}
			break;
		default:
		 	goto END_WHILE;
		}/* end switch*/

		if(status_msg){
			print_error(scrp, status_msg);
			status_msg = NULL;
		}

		move(LINES-1,COLS-1);
		touchwin(scrp);
		wrefresh(scrp);
		ch = getch();
		switch(ch){
		case NT_KEY_UP:
		case KEY_UP:
		case NT_KEY_DOWN:
		case KEY_DOWN:
		case NT_KEY_LEFT:
		case NT_KEY_RIGHT:
		case KEY_RIGHT:
		case NT_KEY_PAGEUP:
		case KEY_PPAGE:
		case NT_KEY_PAGEDOWN:
		case KEY_NPAGE:
		case NT_KEY_SELECT:
		case NT_KEY_ADD:
		case NT_KEY_DEL:
		case NT_KEY_BOTTOM:
		case KEY_END:
		case NT_KEY_REFRESH:
			break;
		case NT_KEY_CLOSE:
		case KEY_LEFT:
			wclear(scrp);
			break;
		case KEY_HOME:
			ch = NT_KEY_COMMAND1;
			nt_add_wch(scrp,ch,0);
			strncpy(buf,"1",2);
			break;
		case NT_KEY_COMMAND1:
		case NT_KEY_COMMAND2:
		case NT_KEY_COMMAND3:
			echo();
			nocbreak();
			wch = ch;
			move(LINES-1,0);
			nt_add_wch(scrp,wch,0);
			getstr(buf);
			cbreak();
			noecho();
			break;
		case NT_KEY_ERASE:
			wclear(scrp);
			break;
		case NT_KEY_QUIT:
			result = TRUE;
			goto END_WHILE;
		}/* end switch*/
	}/* end while */
END_WHILE:
	if(cookiep)
		nt_unload_cookie(cookiep);
	if(marup)
		nt_maru_2ch_free(marup);
	if(writep)
		nt_write_data_free(writep);
	free_board_menu_ctx(bwinp->data);
	free_threadlist_ctx(twinp->data);
	free_reslist_ctx(rwinp->data);
	if(search_winp){
		if(search_winp->data)
			free_search_thread_ctx(search_winp->data);
		nt_disp_win_free(search_winp);
	}
	if(favorite_winp){
		if(favorite_winp->data)
			free_favorite_ctx(favorite_winp->data);
		nt_disp_win_free(favorite_winp);
	}
	nt_disp_win_free(bwinp);
	nt_disp_win_free(twinp);
	nt_disp_win_free(rwinp);
	
	return result;
}

static BOOL set_sel_item(nt_2ch_model_tp modelp,
			nt_2ch_selected_item_tp sel_itemp, 
			nt_searched_thread_tp sel_threadp, const wchar_t** errorpp)
{
	nt_category_tp categoryp;
	nt_board_tp boardp;
	nt_thread_tp threadp;
	
	assert(sel_threadp);
	assert(sel_threadp->board_name);
	//assert(sel_threadp->dat_name);
	
	boardp = nt_get_board_by_name(
			modelp, sel_threadp->board_name, &categoryp);
	if(!boardp || !categoryp){
		*errorpp = L"板情報が見つかりませんでした";
		return FALSE;
	}
		
	sel_itemp->selected_categoryp = categoryp;
	sel_itemp->selected_boardp = boardp;
	sel_itemp->selected_threadp = NULL;
	
	if(!boardp->threadlistp){
		if(!nt_read_board(sel_itemp) 
				|| !boardp->threadlistp){
			*errorpp = L"板が開けませんでした";
			return FALSE;
		}
	}
	if(!sel_threadp->dat_name)
		return TRUE;
	
	threadp = nt_get_thread_by_dat_name(
			boardp->threadlistp, sel_threadp->dat_name);
	if(!threadp){
		*errorpp = L"スレッド情報が見つかりませんでした";
		return FALSE;
	}
	
	sel_itemp->selected_threadp = threadp;
	
	if(!nt_read_thread(sel_itemp)){
		*errorpp = L"スレッドが開けませんでした";
		return FALSE;
	}
	
	return TRUE;
}


static int draw_title(WINDOW *wp, wchar_t *title, attr_t attr)
{
	move(0,0);
	int  num;
	num = nt_get_wc_count_within_colmns(title, COLS-1);
	if(num == 0)
		return 0;
	
	nt_add_wnch(wp, L' ', attr, COLS);
	move(0,1);
	nt_add_wnstr(wp, title, WA_REVERSE, COLS - 1);
	return 1;
}



static void print_error(WINDOW *wp, const wchar_t *msg)
{
	move(LINES-1,0);
	nt_add_wstr(wp, msg, 0);
}

