/* 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 <stdlib.h>
#include <assert.h>

#define MODEL_2CH_PRIVATE_DATA

#include "utils/nt_std_t.h"
#include "utils/text.h"
#include "html/html_string.h"
#include "_2ch/model_2ch.h"


static const wchar_t *NT_2CH_MODEL_MUTEX_KEY = L"root_2ch_data";

nt_2ch_model_handle nt_2ch_model_alloc()
{
	nt_2ch_model_tp modelp;
	modelp = (nt_2ch_model_tp)malloc(sizeof(nt_2ch_model_t));
	if(!modelp)
		return NULL;
	modelp->handle.chk_sum = NT_2CH_MODEL_CHK_SUM;
	modelp->ref_count = 1;
	modelp->key = NT_2CH_MODEL_MUTEX_KEY;
	modelp->categorylistp = NULL;
	return &modelp->handle;
}

nt_category_handle nt_category_alloc(
		nt_2ch_model_handle h_model, const wchar_t *name)
{
	nt_link_tp linkp;
	nt_2ch_model_tp modelp;
	nt_category_tp categoryp;
	
	assert(h_model);
	assert(h_model->chk_sum == NT_2CH_MODEL_CHK_SUM);
	
	modelp = (nt_2ch_model_tp)h_model;
	categoryp = malloc(sizeof(nt_category_t));
	if(!categoryp)
		return NULL;

	categoryp->name = nt_w_trim(name);
	if(!categoryp->name){
		free(categoryp);
		return NULL;
	}
	categoryp->boardlistp = NULL;
	linkp = nt_link_add_data(modelp->categorylistp, categoryp);
	if(!linkp){
		free(categoryp->name);
		free(categoryp);
		return NULL;
	}
	if(modelp->categorylistp == NULL)
		modelp->categorylistp = linkp;
	categoryp->parent = modelp;
	categoryp->handle.chk_sum = NT_2CH_CATEGORY_CHK_SUM;
	categoryp->ref_count = 2;
	return &categoryp->handle;
}

nt_board_handle nt_board_alloc(
		nt_category_handle h_category, const wchar_t *name,
		const wchar_t *addressp)
{
	nt_link_tp linkp;
	nt_category_tp categoryp;
	nt_board_tp boardp;
	
	assert(h_category);
	assert(h_category->chk_sum == NT_2CH_CATEGORY_CHK_SUM);
	
	categoryp = (nt_category_tp)h_category;
	
	boardp = malloc(sizeof(nt_board_t));
	if(!boardp)
		return NULL;

	boardp->name = nt_w_trim(name);
	if(!boardp->name){
		free(boardp);
		return NULL;
	}
	boardp->address = nt_w_trim_quotes(addressp);
	if(!boardp->address){
		free(boardp->name);
		free(boardp);
		return NULL;
	}
	boardp->threadlistp = NULL;
	linkp = nt_link_add_data(categoryp->boardlistp, boardp);
	if(!linkp){
		free(boardp->address);
		free(boardp->name);
		free(boardp);
		return NULL;
	}
	if(categoryp->boardlistp == NULL)
		categoryp->boardlistp = linkp;
	boardp->handle.chk_sum = NT_2CH_BOARD_CHK_SUM;
	boardp->ref_count = 2;
	boardp->parent = categoryp;
	return &boardp->handle;
}

nt_thread_handle nt_thread_alloc(
		nt_board_handle h_board, const wchar_t *name,
		const wchar_t *file_name, int num_res)
{
	nt_board_tp boardp;
	nt_thread_tp threadp;
	nt_link_tp linkp;
	
	assert(h_board);
	assert(h_board->chk_sum == NT_2CH_BOARD_CHK_SUM);
	boardp = (nt_board_tp)h_board;
	
	threadp = malloc(sizeof(nt_thread_t));
	if(!threadp)
		return NULL;

	threadp->name = nt_w_trim(name);
	if(!threadp->name){
		free(threadp);
		return NULL;
	}
	threadp->file_name = nt_w_trim_quotes(file_name);
	if(!threadp->file_name){
		free(threadp->name);
		free(threadp);
		return NULL;
	}
	threadp->num_res = num_res;
	threadp->reslistp = NULL;

	linkp = nt_link_add_data(boardp->threadlistp, threadp);
	if(!linkp){
		free(threadp->file_name);
		free(threadp->name);
		free(threadp);
		return NULL;
	}
	if(boardp->threadlistp == NULL)
		boardp->threadlistp = linkp;

	threadp->handle.chk_sum = NT_2CH_THREAD_CHK_SUM;
	threadp->ref_count = 2;
	threadp->parent = boardp;
	threadp->seq_no = nt_link_num(boardp->threadlistp);
	return &threadp->handle;
}
nt_thread_handle nt_thread_dummy_alloc(
		const wchar_t *name, const wchar_t *file_name, int num_res)
{
	nt_thread_tp threadp;
	
	threadp = malloc(sizeof(nt_thread_t));
	if(!threadp)
		return NULL;

	threadp->name = nt_w_trim(name);
	if(!threadp->name){
		free(threadp);
		return NULL;
	}
	threadp->file_name = nt_w_trim_quotes(file_name);
	if(!threadp->file_name){
		free(threadp->name);
		free(threadp);
		return NULL;
	}
	threadp->num_res = num_res;
	threadp->reslistp = NULL;

	threadp->handle.chk_sum = NT_2CH_THREAD_CHK_SUM;
	threadp->ref_count = 1;
	threadp->parent = NULL;
	threadp->seq_no = -1;
	return &threadp->handle;
}

nt_res_handle nt_res_alloc(nt_thread_handle h_thread, 
		const wchar_t *name, const wchar_t *mail, 
		const wchar_t *misc, const wchar_t *msg)
{
	nt_thread_tp threadp;
	nt_res_tp resp;
	nt_link_tp linkp;
	wchar_t *cptr;
	
	assert(h_thread);
	assert(h_thread->chk_sum == NT_2CH_THREAD_CHK_SUM);
	threadp= (nt_thread_tp)h_thread;
	
	resp = (nt_res_tp)malloc(sizeof(nt_res_t));
	if(!resp)
		return NULL;

	cptr = nt_decord_html_entity(name);
	resp->name = cptr;
	if(!resp->name){
		free(resp);
		return NULL;
	}
	resp->mail = nt_w_trim(mail);
	if(!resp->mail){
		free(resp);
		return NULL;
	}
	resp->misc = nt_w_trim(misc);
	if(!resp->misc){
		free(resp);
		return NULL;
	}
	resp->msg = nt_w_trim(msg);
	if(!resp->msg){
		free(resp);
		return NULL;
	}

	linkp = nt_link_add_data(threadp->reslistp, resp);
	if(!linkp){
		free(resp->name);
		free(resp->mail);
		free(resp->misc);
		free(resp->msg);
		free(resp);
		return NULL;
	}
	if(threadp->reslistp == NULL)
		threadp->reslistp = linkp;
	threadp->num_res++;
	resp->handle.chk_sum = NT_2CH_RES_CHK_SUM;
	resp->ref_count = 2;
	resp->parent = threadp;
	return &resp->handle;
}

int nt_2ch_model_add_ref(nt_2ch_model_handle h_model)
{
	nt_2ch_model_tp modelp;

	assert(h_model);
	assert(h_model->chk_sum == NT_2CH_MODEL_CHK_SUM);
	modelp = (nt_2ch_model_tp)h_model;
	assert(modelp->ref_count > 0);
	return ++modelp->ref_count;
}

int nt_2ch_model_release_ref(nt_2ch_model_handle h_model)
{
	nt_2ch_model_tp modelp;
	nt_category_tp categoryp;
	nt_link_tp linkp, nextp;

	assert(h_model);
	assert(h_model->chk_sum == NT_2CH_MODEL_CHK_SUM);
	modelp = (nt_2ch_model_tp)h_model;
	assert(modelp->ref_count > 0);
	
	if(0 != --modelp->ref_count){
		return modelp->ref_count;
	}
	
	if(modelp->categorylistp){
		linkp = modelp->categorylistp;
		do{
			nextp = linkp->next;
			categoryp = (nt_category_tp)linkp->data;
			categoryp->parent = NULL;
			nt_category_release_ref(&categoryp->handle);
			free(linkp);
			linkp = nextp;
		}while(linkp != modelp->categorylistp);
	}
	free(modelp);
	return 0;
}

int nt_category_add_ref(nt_category_handle h_category)
{
	nt_category_tp categoryp;

	assert(h_category);
	assert(h_category->chk_sum == NT_2CH_CATEGORY_CHK_SUM);
	categoryp = (nt_category_tp)h_category;
	assert(categoryp->ref_count > 0);
	return ++categoryp->ref_count;
}

int nt_category_release_ref(nt_category_handle h_category)
{
	nt_category_tp categoryp;
	nt_board_tp boardp;
	nt_link_tp linkp, nextp;

	assert(h_category);
	assert(h_category->chk_sum == NT_2CH_CATEGORY_CHK_SUM);
	categoryp = (nt_category_tp)h_category;
	assert(categoryp->ref_count > 0);
	
	if(0 != --categoryp->ref_count){
		return categoryp->ref_count;
	}
	if(categoryp->boardlistp){
		linkp = categoryp->boardlistp;
		do{
			nextp = linkp->next;
			boardp = (nt_board_tp)linkp->data;
			boardp->parent = NULL;
			nt_board_release_ref(&boardp->handle);
			free(linkp);
			linkp = nextp;
		}while(linkp != categoryp->boardlistp);
	}
	free(categoryp->name);
	free(categoryp);
	return 0;
}

int nt_board_add_ref(nt_board_handle h_board)
{
	nt_board_tp boardp;

	assert(h_board);
	assert(h_board->chk_sum == NT_2CH_BOARD_CHK_SUM);
	boardp = (nt_board_tp)h_board;
	assert(boardp->ref_count > 0);
	return ++boardp->ref_count;
}

int nt_board_release_ref(nt_board_handle h_board)
{
	nt_board_tp boardp;
	nt_thread_tp threadp;
	nt_link_tp linkp, nextp;

	assert(h_board);
	assert(h_board->chk_sum == NT_2CH_BOARD_CHK_SUM);
	boardp = (nt_board_tp)h_board;
	assert(boardp->ref_count > 0);
	if(0 != --boardp->ref_count){
		return boardp->ref_count;
	}
	if(boardp->threadlistp){
		linkp = boardp->threadlistp;
		do{
			nextp = linkp->next;
			threadp = (nt_thread_tp)linkp->data;
			threadp->parent = NULL;
			nt_thread_release_ref(&threadp->handle);
			free(linkp);
			linkp = nextp;
		}while(linkp != boardp->threadlistp);
	}
	free(boardp->address);
	free(boardp->name);
	free(boardp);
	return 0;
}

void nt_board_clear_children(nt_board_handle h_board)
{
	nt_board_tp boardp;
	nt_thread_tp threadp;
	nt_link_tp linkp, nextp;

	assert(h_board);
	assert(h_board->chk_sum == NT_2CH_BOARD_CHK_SUM);
	boardp = (nt_board_tp)h_board;
	assert(boardp->ref_count > 0);
	
	if(boardp->threadlistp){
		linkp = boardp->threadlistp;
		do{
			nextp = linkp->next;
			threadp = (nt_thread_tp)linkp->data;
			threadp->parent = NULL;
			nt_thread_release_ref(&threadp->handle);
			free(linkp);
			linkp = nextp;
		}while(linkp != boardp->threadlistp);
		boardp->threadlistp = NULL;
	}
}


int nt_thread_add_ref(nt_thread_handle h_thread)
{
	nt_thread_tp threadp;

	assert(h_thread);
	assert(h_thread->chk_sum == NT_2CH_THREAD_CHK_SUM);
	threadp = (nt_thread_tp)h_thread;
	assert(threadp->ref_count > 0);
	return ++threadp->ref_count;
}

int nt_thread_release_ref(nt_thread_handle h_thread)
{
	nt_thread_tp threadp;
	nt_res_tp resp;
	nt_link_tp linkp, nextp;

	assert(h_thread);
	assert(h_thread->chk_sum == NT_2CH_THREAD_CHK_SUM);
	threadp = (nt_thread_tp)h_thread;
	assert(threadp->ref_count > 0);
	
	if(0 != --threadp->ref_count){
		return threadp->ref_count;
	}
	if(threadp->reslistp){
		linkp = threadp->reslistp;
		do{
			nextp = linkp->next;
			resp = (nt_res_tp)linkp->data;
			resp->parent = NULL;
			nt_res_release_ref(&resp->handle);
			free(linkp);
			linkp = nextp;
		}while(linkp != threadp->reslistp);
	}
	free(threadp->file_name);
	free(threadp->name);
	free(threadp);
	return 0;
}

void nt_thread_clear_children(nt_thread_handle h_thread)
{
	nt_thread_tp threadp;
	nt_res_tp resp;
	nt_link_tp linkp, nextp;

	assert(h_thread);
	assert(h_thread->chk_sum == NT_2CH_THREAD_CHK_SUM);
	threadp = (nt_thread_tp)h_thread;
	assert(threadp->ref_count > 0);
	
	if(threadp->reslistp){
		linkp = threadp->reslistp;
		do{
			nextp = linkp->next;
			resp = (nt_res_tp)linkp->data;
			resp->parent = NULL;
			nt_res_release_ref(&resp->handle);
			free(linkp);
			linkp = nextp;
		}while(linkp != threadp->reslistp);
		threadp->reslistp = NULL;
	}
}

int nt_res_add_ref(nt_res_handle h_res)
{
	nt_res_tp resp;

	assert(h_res);
	assert(h_res->chk_sum == NT_2CH_RES_CHK_SUM);
	resp = (nt_res_tp)h_res;
	assert(resp->ref_count > 0);
	return ++resp->ref_count;
}
int nt_res_release_ref(nt_res_handle h_res)
{
	nt_res_tp resp;

	assert(h_res);
	assert(h_res->chk_sum == NT_2CH_RES_CHK_SUM);
	resp = (nt_res_tp)h_res;
	assert(resp->ref_count > 0);
	
	if(0 != --resp->ref_count){
		return resp->ref_count;
	}
	free(resp->name);
	free(resp->mail);
	free(resp->misc);
	free(resp->msg);
	free(resp);
	return 0;
}

nt_write_data_handle nt_write_data_alloc()
{
	nt_write_data_tp writep
		= (nt_write_data_tp)calloc(1,
				sizeof(nt_write_data_t));
	if(!writep)
		return NULL;
	writep->handle.chk_sum = NT_2CH_WRITE_DATA_CHK_SUM;
	writep->ref_count = 1;
	return &(writep->handle);
}

int nt_write_data_add_ref(nt_write_data_handle h_write_data)
{
	nt_write_data_tp write_datap;

	assert(h_write_data);
	assert(h_write_data->chk_sum == NT_2CH_WRITE_DATA_CHK_SUM);
	write_datap = (nt_write_data_tp)h_write_data;
	assert(write_datap->ref_count > 0);
	return ++write_datap->ref_count;
}
int nt_write_data_release_ref(nt_write_data_handle h_write_data)
{
	nt_write_data_tp write_datap;

	assert(h_write_data);
	assert(h_write_data->chk_sum == NT_2CH_WRITE_DATA_CHK_SUM);
	write_datap = (nt_write_data_tp)h_write_data;
	assert(write_datap->ref_count > 0);
	if(0 != --write_datap->ref_count){
		return write_datap->ref_count;
	}
	if(write_datap->name)
		free(write_datap->name);
	if(write_datap->mail)
		free(write_datap->mail);
	if(write_datap->msg)
		free(write_datap->msg);
	if(write_datap->result_html)
		free(write_datap->result_html);
	if(write_datap->status_msg)
		free(write_datap->status_msg);
	if(write_datap->cookies)
		nt_all_link_free(write_datap->cookies, free);

	free(write_datap);
	return 0;
}

nt_2ch_selected_item_handle nt_2ch_selected_item_alloc()
{
	nt_2ch_selected_item_tp selectp;
	
	selectp = (nt_2ch_selected_item_tp)malloc(sizeof(nt_2ch_selected_item_t));
	if(!selectp)
		return NULL;
	selectp->handle.chk_sum = NT_2CH_SELECTED_ITEM_CHK_SUM;
	selectp->ref_count = 1;
	selectp->selected_categoryp = NULL;
	selectp->selected_boardp = NULL;
	selectp->selected_threadp = NULL;
	return &selectp->handle;
	
}
int nt_2ch_selected_item_add_ref(nt_2ch_selected_item_handle h_select)
{
	nt_2ch_selected_item_tp selectp;
	assert(h_select);
	assert(h_select->chk_sum == NT_2CH_SELECTED_ITEM_CHK_SUM);
	selectp = (nt_2ch_selected_item_tp)h_select;
	assert(selectp->ref_count > 0);
	return ++selectp->ref_count;
}
int nt_2ch_selected_item_release_ref(nt_2ch_selected_item_handle h_select)
{
	nt_2ch_selected_item_tp selectp;
	assert(h_select);
	assert(h_select->chk_sum == NT_2CH_SELECTED_ITEM_CHK_SUM);
	selectp = (nt_2ch_selected_item_tp)h_select;
	assert(selectp->ref_count > 0);
	if(0 !=  --selectp->ref_count)
		return selectp->ref_count;
	
	if(selectp->selected_categoryp != NULL)
		nt_category_release_ref(&selectp->selected_categoryp->handle);
	if(selectp->selected_boardp != NULL)
		nt_board_release_ref(&selectp->selected_boardp->handle);
	if(selectp->selected_threadp != NULL)
		nt_thread_release_ref(&selectp->selected_threadp->handle);
	
	free(selectp);
	return 0;
}

