package jp.juggler.subwaytooter;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v4.os.AsyncTaskCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.view.Gravity;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringBufferInputStream;
import java.util.HashMap;
import java.util.HashSet;

import jp.juggler.subwaytooter.api.TootApiClient;
import jp.juggler.subwaytooter.api.TootApiResult;
import jp.juggler.subwaytooter.api.entity.TootAccount;
import jp.juggler.subwaytooter.api.entity.TootStatus;
import jp.juggler.subwaytooter.dialog.AccountPicker;
import jp.juggler.subwaytooter.dialog.LoginForm;
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.HTMLDecoder;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.util.Utils;
import okhttp3.Request;
import okhttp3.RequestBody;

public class ActMain extends AppCompatActivity
	implements NavigationView.OnNavigationItemSelectedListener {
	public static final LogCategory log = new LogCategory( "ActMain" );
	
	static boolean update_at_resume = false;
	
//	@Override
//	protected void attachBaseContext(Context newBase) {
//		super.attachBaseContext( CalligraphyContextWrapper.wrap(newBase));
//	}
	
	@Override
	protected void onCreate( Bundle savedInstanceState ){
		super.onCreate( savedInstanceState );
		requestWindowFeature( Window.FEATURE_NO_TITLE );
		
		initUI();
		loadColumnList();
	}
	
	@Override
	protected void onDestroy(){
		super.onDestroy();
	}
	
	@Override
	protected void onResume(){
		super.onResume();
		HTMLDecoder.link_callback = link_click_listener;
		
		// アカウント設定から戻ってきたら、カラムを消す必要があるかもしれない
		int size =pager_adapter.getCount();
		for(int i=size-1;i>=0;--i){
			Column column = pager_adapter.getColumn( i );
			SavedAccount sa = SavedAccount.loadAccount( log, column.access_info.db_id );
			if( sa == null ){
				pager_adapter.removeColumn( pager,column );
			}
		}
		
		
		if(update_at_resume){
			update_at_resume = false;
			// TODO: 各カラムを更新する
		}
		
		if( pager_adapter.getCount() == 0){
			llEmpty.setVisibility( View.VISIBLE );
		}
	}
	
	@Override
	protected void onPause(){
		HTMLDecoder.link_callback = null;
		saveColumnList();
		super.onPause();
	}
	
	@Override
	public void onBackPressed(){
		DrawerLayout drawer = (DrawerLayout) findViewById( R.id.drawer_layout );
		if( drawer.isDrawerOpen( GravityCompat.START ) ){
			drawer.closeDrawer( GravityCompat.START );
		}else if( ! pager_adapter.column_list.isEmpty() ){
			performColumnClose( false,pager_adapter.getColumn( pager.getCurrentItem() ) );
		}else{
			super.onBackPressed();
		}
	}
	
	@Override
	public boolean onCreateOptionsMenu( Menu menu ){
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate( R.menu.act_main, menu );
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected( MenuItem item ){
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		
		//noinspection SimplifiableIfStatement
		if( id == R.id.action_settings ){
			return true;
		}
		
		return super.onOptionsItemSelected( item );
	}
	
	@SuppressWarnings("StatementWithEmptyBody")
	@Override
	public boolean onNavigationItemSelected( MenuItem item ){
		// Handle navigation view item clicks here.
		int id = item.getItemId();
		
		if( id == R.id.nav_account_add ){
			performAccountAdd();
		}else if( id == R.id.nav_add_tl_home ){
			performAddTimeline( Column.TYPE_TL_HOME );
		}else if( id == R.id.nav_add_tl_local ){
			performAddTimeline( Column.TYPE_TL_LOCAL );
		}else if( id == R.id.nav_add_tl_federate ){
			performAddTimeline( Column.TYPE_TL_FEDERATE );
			
		}else if( id == R.id.nav_add_favourites ){
			performAddTimeline( Column.TYPE_TL_FAVOURITES );
//		}else if( id == R.id.nav_add_reports ){
//			performAddTimeline(Column.TYPE_TL_REPORTS );
		}else if( id == R.id.nav_add_statuses ){
			performAddTimeline( Column.TYPE_TL_STATUSES );
		}else if( id == R.id.nav_add_notifications ){
			performAddTimeline( Column.TYPE_TL_NOTIFICATIONS );
			
		}else if( id == R.id.nav_app_setting ){
			performAppSetting(  );
		}else if( id == R.id.nav_account_setting ){
			performAccountSetting();
		}else if( id == R.id.nav_column_list ){
			performColumnList();
			
			// Handle the camera action
//		}else if( id == R.id.nav_gallery ){
//
//		}else if( id == R.id.nav_slideshow ){
//
//		}else if( id == R.id.nav_manage ){
//
//		}else if( id == R.id.nav_share ){
//
//		}else if( id == R.id.nav_send ){
			
		}
		
		DrawerLayout drawer = (DrawerLayout) findViewById( R.id.drawer_layout );
		drawer.closeDrawer( GravityCompat.START );
		return true;
	}
	
	ViewPager pager;
	ColumnPagerAdapter pager_adapter;
	View llEmpty;
	
	void initUI(){
		setContentView( R.layout.act_main );
		llEmpty = findViewById( R.id.llEmpty );

//		// toolbar
//		Toolbar toolbar = (Toolbar) findViewById( R.id.toolbar );
//		setSupportActionBar( toolbar );
		
		// navigation drawer
		final DrawerLayout drawer = (DrawerLayout) findViewById( R.id.drawer_layout );
//		ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
//			this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close );
//		drawer.addDrawerListener( toggle );
//		toggle.syncState();
		
		NavigationView navigationView = (NavigationView) findViewById( R.id.nav_view );
		navigationView.setNavigationItemSelectedListener( this );
		
		// floating action button
		FloatingActionButton fabToot = (FloatingActionButton) findViewById( R.id.fabToot );
		fabToot.setOnClickListener( new View.OnClickListener() {
			@Override
			public void onClick( View view ){
				performTootButton();
			}
		} );
		// floating action button
		FloatingActionButton fabMenu = (FloatingActionButton) findViewById( R.id.fabMenu );
		fabMenu.setOnClickListener( new View.OnClickListener() {
			@Override
			public void onClick( View view ){
				if( ! drawer.isDrawerOpen( Gravity.START ) ){
					drawer.openDrawer( Gravity.START );
				}
			}
		} );
		
		// ViewPager
		pager = (ViewPager) findViewById( R.id.viewPager );
		pager_adapter = new ColumnPagerAdapter( this );
		pager.setAdapter( pager_adapter );
	}
	
	public void performAccountAdd(){
		LoginForm.showLoginForm( this, new LoginForm.LoginFormCallback() {
			
			@Override
			public void startLogin( final Dialog dialog, final String instance, final String user_mail, final String password ){
				
				final ProgressDialog progress = new ProgressDialog( ActMain.this );
				
				final AsyncTask< Void, String, TootApiResult > task = new AsyncTask< Void, String, TootApiResult >() {
					
					long row_id;
					
					@Override
					protected TootApiResult doInBackground( Void... params ){
						TootApiClient api_client = new TootApiClient( ActMain.this, new TootApiClient.Callback() {
							@Override
							public boolean isApiCancelled(){
								return isCancelled();
							}
							
							@Override
							public void publishApiProgress( final String s ){
								Utils.runOnMainThread( new Runnable() {
									@Override
									public void run(){
										progress.setMessage( s );
									}
								} );
							}
						} );
						
						api_client.setUserInfo( instance, user_mail, password );
						
						TootApiResult result = api_client.request( "/api/v1/accounts/verify_credentials" );
						if( result != null && result.object != null ){
							TootAccount ta = TootAccount.parse( log, result.object );
							String user = ta.username +"@" + instance;
							this.row_id = SavedAccount.insert( instance, user, result.object ,result.token_info );
						}
						return result;
					}
					
					@Override
					protected void onPostExecute( TootApiResult result ){
						progress.dismiss();
						
						if( result == null ){
							// cancelled.
						}else if( result.object == null ){
							Utils.showToast( ActMain.this, true, result.error );
							log.e( result.error );
						}else{
							SavedAccount account = SavedAccount.loadAccount( log, row_id );
							if( account != null ){
								ActMain.this.onAccountUpdated( account );
								dialog.dismiss();
							}
						}
					}
				};
				progress.setIndeterminate( true );
				progress.setCancelable( true );
				progress.setOnCancelListener( new DialogInterface.OnCancelListener() {
					@Override
					public void onCancel( DialogInterface dialog ){
						task.cancel( true );
					}
				} );
				progress.show();
				AsyncTaskCompat.executeParallel( task );
			}
		} );
		
	}
	
	private void onAccountUpdated( SavedAccount data ){
		Utils.showToast( this, false, R.string.account_confirmed );
		//
		llEmpty.setVisibility( View.GONE );
		//
		Column col = new Column( this, data, Column.TYPE_TL_HOME );
		int idx = pager_adapter.addColumn( pager, col );
		pager.setCurrentItem( idx );
	}
	
	public void performColumnClose( boolean bConfirm,final Column column ){
		if(! bConfirm ){
			new AlertDialog.Builder( this )
				.setTitle( R.string.confirm )
				.setMessage( R.string.close_column )
				.setNegativeButton( R.string.cancel, null )
				.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
					@Override
					public void onClick( DialogInterface dialog, int which ){
						performColumnClose( true,column );
					}
				} )
				.show();
			return;
		}
		int page_showing = pager.getCurrentItem();
		int page_delete = pager_adapter.column_list.indexOf( column );
		pager_adapter.removeColumn( pager, column );
		if( pager_adapter.getCount() == 0 ){
			llEmpty.setVisibility( View.VISIBLE );
		}else if( page_showing > 0 && page_showing == page_delete ){
			pager.setCurrentItem( page_showing-1 ,true);
		}
	}
	
	
	void performOpenUser(SavedAccount access_info,TootAccount user){
		llEmpty.setVisibility( View.GONE );
		//
		Column col = new Column( ActMain.this, access_info, Column.TYPE_TL_STATUSES, user.id );
		pager.setCurrentItem( pager_adapter.addColumn( pager, col ) ,true);
	}
	
	private void performAddTimeline( final int type, final Object... params ){
		AccountPicker.pick( this, new AccountPicker.AccountPickerCallback() {
			@Override
			public void onAccountPicked( SavedAccount ai ){
				llEmpty.setVisibility( View.GONE );
				//
				Column col = new Column( ActMain.this, ai, type, ai.id, params );
				int idx = pager_adapter.addColumn( pager, col );
				pager.setCurrentItem( idx ,true);
			}
		} );
	}
	
	public void openBrowser( String url ){
		openChromeTab( url );
	}
	
	public void openChromeTab( String url ){
		try{
			// ビルダーを使って表示方法を指定する
			CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
			builder.setToolbarColor( ContextCompat.getColor( this, R.color.colorPrimary ) ).setShowTitle( true );
			
			// CustomTabsでURLをひらくIntentを発行
			CustomTabsIntent customTabsIntent = builder.build();
			customTabsIntent.launchUrl( this, Uri.parse( url ) );
			
		}catch( Throwable ex ){
			// ex.printStackTrace();
			log.e( ex, "openChromeTab failed. url=%s",url );
		}
	}
	
	final HTMLDecoder.LinkClickCallback link_click_listener = new HTMLDecoder.LinkClickCallback() {
		@Override
		public void onClickLink( String url ){
			openChromeTab( url );
		}
	};
	
	static final String FILE_COLUMN_LIST = "column_list";
	
	private void loadColumnList(){
		try{
			InputStream is = openFileInput( FILE_COLUMN_LIST );
			try{
				ByteArrayOutputStream bao = new ByteArrayOutputStream( is.available() );
				byte[] tmp = new byte[ 4096 ];
				for( ; ; ){
					int r = is.read( tmp, 0, tmp.length );
					if( r <= 0 ) break;
					bao.write( tmp, 0, r );
				}
				JSONArray array = new JSONArray( Utils.decodeUTF8( bao.toByteArray() ) );
				for( int i = 0, ie = array.length() ; i < ie ; ++ i ){
					try{
						JSONObject src = array.optJSONObject( i );
						Column col = new Column( ActMain.this, src );
						pager_adapter.addColumn( pager, col );
					}catch( Throwable ex ){
						ex.printStackTrace();
					}
				}
			}finally{
				is.close();
			}
		}catch( FileNotFoundException ignored ){
		}catch( Throwable ex ){
			ex.printStackTrace();
			Utils.showToast( this, ex, "loadColumnList failed." );
		}
		
		if( pager_adapter.column_list.size() > 0 ){
			llEmpty.setVisibility( View.GONE );
		}
	}
	
	private void saveColumnList(){
		JSONArray array = new JSONArray();
		for( Column column : pager_adapter.column_list ){
			try{
				JSONObject item = new JSONObject();
				column.encodeJSON( item );
				array.put( item );
			}catch( JSONException ex ){
				ex.printStackTrace();
			}
		}
		try{
			OutputStream os = openFileOutput( FILE_COLUMN_LIST, MODE_PRIVATE );
			try{
				os.write( Utils.encodeUTF8( array.toString() ) );
			}finally{
				os.close();
			}
		}catch( Throwable ex ){
			ex.printStackTrace();
			Utils.showToast( this, ex, "saveColumnList failed." );
		}
	}
	
	private void performTootButton(){

		Column c = pager_adapter.getColumn( pager.getCurrentItem() );
		if( c != null && c.access_info != null ){
			ActPost.open( this, c.access_info.db_id ,null );
		}
	}
	
	/////////////////////////////////////////////////////////////////////////
	
	private void showColumnMatchAccount( SavedAccount account ){
		for( Column column : pager_adapter.column_list ){
			if( account.user.equals( column.access_info.user ) ){
				column.fireVisualCallback();
			}
		}
	}
	
	
	/////////////////////////////////////////////////////////////////////////
	// favourite
	
	final HashSet<String> map_busy_fav = new HashSet<>(  );
	
	boolean isBusyFav(SavedAccount account,TootStatus status){
		String busy_key = account.host+":"+ status.id;
		return map_busy_fav.contains(busy_key);
	}
	
	public void performFavourite( final SavedAccount account, final TootStatus status ){
		//
		final String busy_key = account.host+":"+ status.id;
		//
		if( map_busy_fav.contains(busy_key) ){
			Utils.showToast( this,false,R.string.wait_previous_operation );
			return;
		}
		//
		map_busy_fav.add( busy_key );
		//
		new AsyncTask<Void,Void,TootApiResult>(){
			final boolean new_state = ! status.favourited;
			TootStatus new_status;
			
			@Override
			protected TootApiResult doInBackground( Void... params ){
				TootApiClient client = new TootApiClient( ActMain.this, new TootApiClient.Callback() {
					@Override
					public boolean isApiCancelled(){
						return isCancelled();
					}
					
					@Override
					public void publishApiProgress( final String s ){
					}
				} );
				client.setAccount( account );
				
				Request.Builder request_builder = new Request.Builder()
					.post( RequestBody.create(
						TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
						,""
					));
				
				TootApiResult result = client.request(
					(new_state
						? "/api/v1/statuses/"+status.id+"/favourite"
						: "/api/v1/statuses/"+status.id+"/unfavourite"
					)
					, request_builder );
				if( result.object != null ){
					new_status = TootStatus.parse( log,result.object );
				}

				return result;
				
			}
			
			@Override
			protected void onCancelled( TootApiResult result ){
				super.onPostExecute( result );
			}
			
			@Override
			protected void onPostExecute( TootApiResult result ){
				map_busy_fav.remove( busy_key);
				if( new_status  != null ){
					// カウント数は遅延があるみたい
					if( new_state && new_status.favourites_count <= status.favourites_count ){
						// 星つけたのにカウントが上がらないのは違和感あるので、表示をいじる
						new_status.favourites_count = status.favourites_count +1;
					}else if( !new_state && new_status.favourites_count >= status.favourites_count ){
						// 星外したのにカウントが下がらないのは違和感あるので、表示をいじる
						new_status.favourites_count = status.favourites_count -1;
						if( new_status.favourites_count < 0 ){
							new_status.favourites_count = 0;
						}
					}
					for( Column column : pager_adapter.column_list ){
						column.findStatus( account, new_status.id, new Column.StatusEntryCallback() {
							@Override
							public void onIterate( TootStatus status ){
								status.favourited = new_status.favourited;
								status.favourites_count = new_status.favourites_count;
							}
						});
					}
				}else{
					if( result != null) Utils.showToast( ActMain.this,true,result.error );
				}
				showColumnMatchAccount(account);
			}
			
		}.execute();
		showColumnMatchAccount(account);
	}
	

	
	/////////////////////////////////////////////////////////////////////////
	// boost
	final HashSet<String> map_busy_boost = new HashSet<>(  );
	
	boolean isBusyBoost(SavedAccount account,TootStatus status){
		String busy_key = account.host+":"+ status.id;
		return map_busy_boost.contains( busy_key);
	}
	
	public void performBoost( final SavedAccount account, final TootStatus status ,boolean bConfirmed){
		//
		final String busy_key = account.host + ":" + status.id;
		//
		if(map_busy_boost.contains( busy_key ) ){
			Utils.showToast( this, false, R.string.wait_previous_operation );
			return;
		}
		
		if( status.reblogged ){
			// FAVがついているか、FAV操作中はBoostを外せない
			if( isBusyFav( account, status ) || status.favourited ){
				Utils.showToast( this, false, R.string.cant_remove_boost_while_favourited );
				return;
			}
		}else{
			if(!bConfirmed && account.confirm_boost ){
				// TODO: アカウント設定でスキップさせたい
				new AlertDialog.Builder(this)
					.setTitle(R.string.confirm)
					.setMessage(R.string.confirm_boost)
					.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
						@Override
						public void onClick( DialogInterface dialog, int which ){
							performBoost(  account,  status ,true);
						}
					} )
					.setNegativeButton( R.string.cancel,null )
					.show();
				return;
			}
		}

		//
		map_busy_boost.add( busy_key);
		//
		new AsyncTask<Void,Void,TootApiResult>(){
			final boolean new_state = ! status.reblogged;
			TootStatus new_status;
			
			@Override
			protected TootApiResult doInBackground( Void... params ){
				TootApiClient client = new TootApiClient( ActMain.this, new TootApiClient.Callback() {
					@Override
					public boolean isApiCancelled(){
						return isCancelled();
					}
					
					@Override
					public void publishApiProgress( final String s ){
					}
				} );
				client.setAccount( account );
				
				Request.Builder request_builder = new Request.Builder()
					.post( RequestBody.create(
						TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED
						,""
					));
				
				TootApiResult result = client.request(
					"/api/v1/statuses/"+status.id+(new_state ? "/reblog" : "/unreblog")
					, request_builder );

				if( result.object != null ){
					// reblog,unreblog のレスポンスは信用ならんのでステータスを再取得する
					result = client.request("/api/v1/statuses/"+status.id);
					if( result.object != null ){
						new_status = TootStatus.parse( log, result.object );
					}
				}
				
				return result;
				
			}
			
			@Override
			protected void onCancelled( TootApiResult result ){
				super.onPostExecute( result );
			}
			
			@Override
			protected void onPostExecute( TootApiResult result ){
				map_busy_boost.remove( busy_key);
				if( new_status  != null ){
					// カウント数は遅延があるみたい
					if( new_status.reblogged && new_status.reblogs_count <= status.reblogs_count ){
						// 星つけたのにカウントが上がらないのは違和感あるので、表示をいじる
						new_status.reblogs_count = status.reblogs_count +1;
					}else if( !new_status.reblogged && new_status.reblogs_count >= status.reblogs_count ){
						// 星外したのにカウントが下がらないのは違和感あるので、表示をいじる
						new_status.reblogs_count = status.reblogs_count -1;
						if( new_status.reblogs_count < 0 ){
							new_status.reblogs_count = 0;
						}
					}
					for( Column column : pager_adapter.column_list ){
						column.findStatus( account, new_status.id, new Column.StatusEntryCallback() {
							@Override
							public void onIterate( TootStatus status ){
								status.reblogged = new_status.reblogged;
								status.reblogs_count = new_status.reblogs_count;
							}
						});
					}
				}else{
					if( result != null) Utils.showToast( ActMain.this,true,result.error );
				}
				showColumnMatchAccount(account);
			}
			
		}.execute();
		
		showColumnMatchAccount(account);
	}
	
	////////////////////////////////////////
	
	public void performMore( SavedAccount account, TootStatus status ){
		// open menu
		// Expand this status
		// Mute user
		// Block user
		// report user
		Utils.showToast( this,false,"not implemented. toot="+status.decoded_content );
	}

	public void performReply( SavedAccount account, TootStatus status ){
		Utils.showToast( this,false,"not implemented. toot="+status.decoded_content );
	}
	
	////////////////////////////////////////
	
	private void performColumnList(){
		
		Utils.showToast( this,false,"not implemented." );
		
	}
	
	private void performAccountSetting(){
		AccountPicker.pick( this, new AccountPicker.AccountPickerCallback() {
			@Override
			public void onAccountPicked( SavedAccount ai ){
				ActAccountSetting.open( ActMain.this, ai);
			}
		} );
	}
	
	private void performAppSetting(){
		Utils.showToast( this,false,"not implemented." );
	}
	
	
}
