#include <wchar.h>
#include <stdlib.h>
#include <vector>
#include <widec.h>
#include "spf.h"

using namespace std;

// 各個の対応を保持する
typedef struct tagBRACKET_PAIR {
	wchar_t Left;
	wchar_t Right;
} BRACKET_PAIR;

static vector< BRACKET_PAIR > CreateBracketPairVec( const wchar_t *p );
static void ExistBracketOnLeftSide( CSyuhituPlugin spf, const vector< BRACKET_PAIR > &rvBracket, int *pIdx, bool *pIsBra );
static void ExistBracketOnRightSide( CSyuhituPlugin spf, const vector< BRACKET_PAIR > &rvBracket, int *pIdx, bool *pIsBra );
static void SearchToLeft( CSyuhituPlugin spf, const BRACKET_PAIR &bp, bool IsCurLeft );
static void SearchToRight( CSyuhituPlugin spf, const BRACKET_PAIR &bp, bool IsCurLeft );

extern "C" void FindBracket( PFT_GetAPIFunction GetAPIFunction )
{
	CSyuhituPlugin spf( GetAPIFunction );
	vector< BRACKET_PAIR > vBracketPair;
	int idx;
	bool IsBra;

	try {
		// 設定を取得する
		const wchar_t *pPrioritySide = spf.GetConfigValue( L"PrioritySide" );
		const wchar_t *pBracketChar = spf.GetConfigValue( L"BracketChar" );

		if ( !pPrioritySide )
			pPrioritySide = L"Left";
		if ( !pBracketChar )
			pBracketChar = L"(){}[]<>";

		vBracketPair = CreateBracketPairVec( pBracketChar );

		// カーソルの近くに括弧があるか否か
		if ( wscasecmp( pPrioritySide, L"Left" ) == 0 ) {
			ExistBracketOnLeftSide( spf, vBracketPair, &idx, &IsBra );
			if ( idx != -1 ) {
				if ( IsBra )
					SearchToRight( spf, vBracketPair[idx], true );
				else
					SearchToLeft( spf, vBracketPair[idx], true );
				return ;
			}
			ExistBracketOnRightSide( spf, vBracketPair, &idx, &IsBra );
			if ( idx != -1 ) {
				if ( IsBra )
					SearchToRight( spf, vBracketPair[idx], false );
				else
					SearchToLeft( spf, vBracketPair[idx], false );
				return ;
			}
		}
		else {
			ExistBracketOnRightSide( spf, vBracketPair, &idx, &IsBra );
			if ( idx != -1 ){
				if ( IsBra )
					SearchToRight( spf, vBracketPair[idx], false );
				else
					SearchToLeft( spf, vBracketPair[idx], false );
				return ;
			}
			ExistBracketOnLeftSide( spf, vBracketPair, &idx, &IsBra );
			if ( idx != -1 ) {
				if ( IsBra )
					SearchToRight( spf, vBracketPair[idx], true );
				else
					SearchToLeft( spf, vBracketPair[idx], true );
				return ;
			}
		}
	}
	catch ( CSP_Err e ) {
		printf( "ERROR\n" );
	};
}

// 各個の対応を保持した配列を構築する
vector< BRACKET_PAIR > CreateBracketPairVec( const wchar_t *p )
{
	BRACKET_PAIR pair;
	bool flg;
	const wchar_t *wp;
	vector< BRACKET_PAIR > rv;

	flg = true;
	wp = p;
	while ( (*wp) ) {
		if ( flg ) {
			pair.Left = (*wp);
			flg = false;
		}
		else {
			pair.Right = (*wp);
			rv.push_back( pair );
			flg = true;
		}
		wp++;
	}	
	return rv;
}

// カーソルの左側に括弧があるか否か
void ExistBracketOnLeftSide( CSyuhituPlugin spf, const vector< BRACKET_PAIR > &rvBracket, int *pIdx, bool *pIsBra )
{
	unsigned long LP, CP;
	int i;
	wchar_t wc;

	(*pIdx) = -1;

	// カーソルの位置を取得
	spf.GetCurPosition( &LP, &CP );

	// カーソルが左端にある場合は、左側にはカーソルは存在し得ない
	if ( CP == 0 ) return ;

	// カーソルの左側の文字を取得する
	wc = spf.GetChar( LP, CP - 1 );

	// 取得した文字がカーソルか否か判断する
	for ( i = 0; i < rvBracket.size(); i++ ) {
		if ( rvBracket[i].Left == wc ) {
			(*pIdx) = i;
			(*pIsBra) = true;
			return ;
		}
		if ( rvBracket[i].Right == wc ) {
			(*pIdx) = i;
			(*pIsBra) = false;
			return ;
		}
	}
}

// カーソルの右側に括弧があるか否か
void ExistBracketOnRightSide( CSyuhituPlugin spf, const vector< BRACKET_PAIR > &rvBracket, int *pIdx, bool *pIsBra )
{
	unsigned long LP, CP;
	unsigned long len;
	int i;
	wchar_t wc;

	(*pIdx) = -1;

	// カーソルの位置を取得
	spf.GetCurPosition( &LP, &CP );
	// カーソルがある行の文字数を取得する
	len = spf.GetCharCount( LP );

	// カーソルが右端にある場合は、左側にはカーソルは存在し得ない
	if ( CP >= len ) return ;

	// カーソルの右側（つまりカーソルのある位置）の文字を取得する
	wc = spf.GetChar( LP, CP );

	// 取得した文字がカーソルか否か判断する
	for ( i = 0; i < rvBracket.size(); i++ ) {
		if ( rvBracket[i].Left == wc ) {
			(*pIdx) = i;
			(*pIsBra) = true;
			return ;
		}
		if ( rvBracket[i].Right == wc ) {
			(*pIdx) = i;
			(*pIsBra) = false;
			return ;
		}
	}
}

// カーソルに位置から左に向かって検索する
// IsCurLeft : 見つかった括弧は、カーソルの左側にあるのか否か
void SearchToLeft( CSyuhituPlugin spf, const BRACKET_PAIR &bp, bool IsCurLeft )
{
	unsigned long CP, LP;
	int cnt;
	wchar_t wc;

	// カーソル位置を取得
	spf.GetCurPosition( &LP, &CP );
	if ( IsCurLeft ) {
		if ( CP == 0 ) {
			if ( LP == 0 ) return ;
			LP--;
			CP = spf.GetCharCount( LP ) - 1;
		}
		CP--;
	}

	cnt = 1;
	while( -1 ) {
		if ( CP == 0 ) {
			if ( LP == 0 ) return ;
			LP--;
			CP = spf.GetCharCount( LP ) - 1;
		}
		else
			CP--;
		// 文字を取得
		wc = spf.GetChar( LP, CP );
		if ( wc == bp.Left ) {
			cnt--;
			if ( cnt == 0 ) {
				// 対応する括弧が見つかった
				spf.SetCurPosition( LP, CP );
				return ;
			}
		}
		if ( wc == bp.Right )
			cnt++;
	};
}

// カーソルの右側に向かって検索する
void SearchToRight( CSyuhituPlugin spf, const BRACKET_PAIR &bp, bool IsCurLeft )
{
	unsigned long CP, LP;
	int cnt;
	unsigned long len;
	unsigned long LineCnt;
	wchar_t wc;

	// カーソル位置を取得
	spf.GetCurPosition( &LP, &CP );

	// 文字数を取得
	len = spf.GetCharCount( LP );

	// 行数を取得
	LineCnt = spf.GetLineCount();

	if ( !IsCurLeft ) {
		if ( CP == len - 1 ) {
			if ( LP == LineCnt - 1 ) return ;
			LP++;
			CP = 0;
			len = spf.GetCharCount( LP );
		}
		else
			CP++;
	}

	cnt = 1;
	while( -1 ) {
		if ( CP == len - 1 ) {
			if ( LP == LineCnt - 1 ) return ;
			LP++;
			CP = 0;
			len = spf.GetCharCount( LP );
		}
		else
			CP++;
		// 文字を取得
		wc = spf.GetChar( LP, CP );
		if ( wc == bp.Right ) {
			cnt--;
			if ( cnt == 0 ) {
				// 対応する括弧が見つかった
				spf.SetCurPosition( LP, CP );
				return ;
			}
		}
		if ( wc == bp.Left )
			cnt++;
	}
}

