/*
 * [The "BSD licence"]
 * Copyright (c) 2017 ZhaoHai
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @author zhaohai
 * @time 2015.10
 * ARSC编辑器主界面
 * */
package zhao.elf.editor;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UnknownFormatConversionException;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.ClipboardManager;
import android.text.Editable;
import android.text.InputFilter;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	/**
	 * 一个用来获取解析后的资源的线程
	 * 
	 * @author zhaohai
	 */
	class GetTask extends AsyncTask<String, Void, Void> {
		// 进度条
		private ProgressDialog dlg;

		// 执行耗时任务
		@Override
		protected Void doInBackground(String... params) {
			if (RESOURCES != null) {
				////////////////////////////////////////////////////////////////
				if (checkChanged()) {
					// 整理RoData
					if (textCategory.getText().toString().equals("dynstr")) {
						elfParser.sortStrData(txtOriginal, txtTranslated, elfParser.ro_items);
					} else { // 整理Dynstr
						elfParser.sortStrData(txtOriginal, txtTranslated, elfParser.dy_items);
					}
					isChanged = true;
				}
				////////////////////////////////////////////////////////////

				txtOriginal.clear();
				txtTranslated.clear();

				for (ResourceHelper resource : RESOURCES.values()) {
					// 获取资源的值
					String VALUE = resource.VALUE;
					// 获取资源类型
					String TYPE = resource.TYPE;

					if (TYPE.equals(params[0])) {
						// 向储存字符串的列表中添加字符串成员
						txtOriginal.add(VALUE);
					}
				}
				initList();
			}
			return null;
		}

		// 耗时任务执行完毕后的事件处理
		@Override
		protected void onPostExecute(Void result) {
			// 隐藏进度条
			dlg.dismiss();
			// 通知数据适配器更新数据
			mAdapter.notifyDataSetInvalidated();
		}

		// 耗时任务开始前执行的任务
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			dlg = new ProgressDialog(MainActivity.this);
			dlg.setCancelable(false);
			dlg.setTitle(R.string.parsing);
			dlg.show();
		}

	}

	/**
	 * @author zhaohai 一个用来解析ARSC的线程
	 */
	class ParseTask extends AsyncTask<InputStream, Integer, String> {
		// 进度条
		private ProgressDialog dlg;
		// 资源回调接口
		private ResourceCallBack callback;

		// 执行耗时任务
		@Override
		protected String doInBackground(InputStream... params) {

			try {
				parseELF(callback, params[0]);
			} catch (UnknownFormatConversionException | IOException e) {
				e.printStackTrace();
				return "failed";
			}
			return getString(R.string.success);
		}

		// 耗时任务执行完毕后的事件处理
		@Override
		protected void onPostExecute(String result) {
			// 隐藏进度条
			dlg.dismiss();
			// 如果返回的结果不是成功
			if (!result.equals(getString(R.string.success))) {
				// 显示错误信息
				showMessage(MainActivity.this, result).show();
				return;
			}
			// 对资源种类列表排序
			Collections.sort(Types);
			// 开启新线程
			AsyncTask<String, Void, Void> getTask = new GetTask();
			getTask.execute(textCategory.getText().toString());
		}

		// 耗时任务开始前执行的任务
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			dlg = new ProgressDialog(MainActivity.this);
			dlg.setCancelable(false);
			dlg.setTitle(R.string.parsing);
			dlg.show();
			textCategory.setText("dynstr");
			// 如果储存资源类型的列表未初始化
			if (Types == null) {
				// 初始化储存资源类型的列表
				Types = new ArrayList<String>();
			}
			// 实现资源回调接口
			callback = new ResourceCallBack() {
				@Override
				public void back(ResourceHelper helper) {
					if (RESOURCES == null) {
						RESOURCES = new LinkedHashMap<String, ResourceHelper>();
					}
					RESOURCES.put(helper.VALUE, helper);
					// 如果资源种类集合中不存在该种类
					if (!Types.contains(helper.TYPE)) {
						// 向其中添加该种类
						Types.add(helper.TYPE);
					}
				}
			};
		}

		// 更新ui界面
		@Override
		protected void onProgressUpdate(Integer... values) {
			dlg.setMessage(String.valueOf(values[0]));
		}

	}

	/**
	 * @author zhaohai 一个用来保存资源文件的线程
	 */
	class SaveFileTask extends AsyncTask<String, String, String> {
		// 进度条
		private ProgressDialog dlg;

		// 执行耗时任务
		@Override
		protected String doInBackground(String... params) {
			try {
				writeELFString((String) params[0]);
			} catch (IOException e) {
				e.printStackTrace();
				return e.toString();
			}
			return getString(R.string.success);
		}

		// 耗时任务执行完毕后的事件处理
		@Override
		protected void onPostExecute(String result) {
			// 隐藏进度条
			dlg.dismiss();
			// 如果返回的结果不是成功
			if (!result.equals(getString(R.string.success))) {
				// 显示错误信息
				showMessage(MainActivity.this, result).show();
				return;
			}
			Toast.makeText(MainActivity.this, "success",0).show();
			finish();
		}

		// 耗时任务开始前执行的任务
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			// 初始化进度条
			dlg = new ProgressDialog(MainActivity.this);
			// 设置进度条标题
			dlg.setTitle(R.string.saving);
			// 设置按进度条外部进度条不消失
			dlg.setCancelable(false);
			// 显示进度条
			dlg.show();
		}

	}

	// 数据适配器
	public class stringListAdapter extends BaseAdapter {

		// 上下文
		private Context mContext;

		// 构造函数
		public stringListAdapter(Context context) {
			super();
			// 获取上下文
			this.mContext = context;
		}

		// 获取数据成员个数
		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return txtOriginal.size();
		}

		// 获取指定条目的内容
		@Override
		public Object getItem(int arg0) {
			// TODO Auto-generated method stub
			return arg0;
		}

		// 获取指定条目的文字
		@Override
		public long getItemId(int arg0) {
			// TODO Auto-generated method stub
			return arg0;
		}

		// 获取View
		@SuppressLint({ "ViewHolder", "InflateParams" })
		@Override
		public View getView(final int position, View view, ViewGroup arg2) {

			// 文本框内容改变的事件监听器
			TextWatcher textWatcher = new TextWatcher() {

				// 文本改变后的事件处理
				@Override
				public void afterTextChanged(Editable s) {
					// 向当前位置添加新的内容,以此实现文本的更新
					txtTranslated.set(position, s.toString());
					isChanged = true;
				}

				// 文本改变之前的事件处理
				@Override
				public void beforeTextChanged(CharSequence s, int start, int count, int after) {
				}

				// 文本改变的事件处理
				@Override
				public void onTextChanged(CharSequence s, int start, int before, int count) {

				}

			};

			// 创建view对象
			view = LayoutInflater.from(mContext).inflate(R.layout.res_string_item, null);
			// 获取显示原来的字符串的控件
			final TextView txtOriginalView = (TextView) view.findViewById(R.id.txtOriginal);
			// 获取用来修改的文本框
			EditText txtTranslatedView = (EditText) view.findViewById(R.id.txtTranslated);

			final String originalStr = txtOriginal.get(position);
			// 显示原来的字符串
			txtOriginalView.setText(originalStr);
			// 显示修改后的字符串
			txtTranslatedView.setText(txtTranslated.get(position));
			txtTranslatedView.setFilters(new InputFilter[] { new InputFilter() {
				@Override
				public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
						int dend) {
					int len = 0;
					boolean more = false;
					do {
						SpannableStringBuilder builder = new SpannableStringBuilder(dest).replace(dstart, dend,
								source.subSequence(start, end));
						len = builder.toString().getBytes().length;
						more = len > originalStr.getBytes().length;
						if (more) {
							end--;
							source = source.subSequence(start, end);
						}
					} while (more);
					return source;
				}
			}});
			// 为文本框设置内容改变的监听器
			txtTranslatedView.addTextChangedListener(textWatcher);
			View.OnLongClickListener longclick_listener = new View.OnLongClickListener() {
				@Override
				public boolean onLongClick(View v) {
					ClipboardManager cm = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
					// 将文本内容放到系统剪贴板里。
					cm.setText(txtOriginalView.getText());
					Toast.makeText(MainActivity.this, "复制成功", Toast.LENGTH_LONG).show();
					return true;
				}
			};
			txtOriginalView.setOnLongClickListener(longclick_listener);
			return view;
		}
	}

	// 存储资源种类的集合
	public static List<String> Types;

	/**
	 * 
	 * @param is
	 * @return
	 * @throws IOException
	 */
	public static byte[] InputStream2ByteArray(InputStream is) throws IOException {
		int count;
		byte[] buffer = new byte[2048];
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		while ((count = is.read(buffer)) != -1) {
			bos.write(buffer, 0, count);
		}
		bos.close();
		return bos.toByteArray();
	}

	// 显示信息的方法
	public static AlertDialog.Builder showMessage(Context activity, String message) {
		return new AlertDialog.Builder(activity).setMessage(message).setNegativeButton(R.string.ok, null)
				.setCancelable(false).setTitle(R.string.error);
	}

	private String fileSrc;

	private Elf elfParser;

	// 存储字符串的集合
	public List<String> txtOriginal = new ArrayList<String>();

	// 存储修改后的字符串的集合
	public List<String> txtTranslated = new ArrayList<String>();

	// 列表控件
	public ListView stringListView;

	// 数据处理器
	public stringListAdapter mAdapter;

	// 存储资源的集合
	private Map<String, ResourceHelper> RESOURCES;

	// 显示资源种类的文本控件
	private TextView textCategory;

	// 字符串是否修改
	public boolean isChanged;

	/**
	 * 文本框内容改变的事件监听器
	 * 
	 * @author zhaohai
	 */
	private TextWatcher textWatcher = new TextWatcher() {

		// 文本改变后的事件处理
		@Override
		public void afterTextChanged(Editable s) {
			// 初始化一个线程用来获取资源
			AsyncTask<String, Void, Void> task = new GetTask();
			// 开启该线程
			task.execute(textCategory.getText().toString());
		}

		// 文本改变之前的事件处理
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
			// TODO Auto-generated method stub
		}

		// 文本改变的事件处理
		@Override
		public void onTextChanged(CharSequence s, int start, int before, int count) {

		}

	};

	// 一些控件的点击事件监听器
	private OnClickListener MyOnClickListener = new OnClickListener() {
		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub
			switch (arg0.getId()) {
			// 点击了资源类型的文本框
			case R.id.textCategory:
				// 弹出一个对话框,列出所有的资源类型
				new AlertDialog.Builder(MainActivity.this).setTitle("")
						.setItems(Types.toArray(new String[Types.size()]), new DialogInterface.OnClickListener() {
							// 对话框上的条目点击的事件监听器
							@Override
							public void onClick(DialogInterface arg0, int arg1) {
								// TODO Auto-generated method stub
								textCategory.setText(Types.get(arg1));
							}
						}).create().show();
				break;
			}
		}
	};

	private boolean checkChanged() {
		for (String str : txtTranslated) {
			if (!str.equals("")) {
				return true;
			}
		}
		return false;
	}

	/** 初始化容器 **/
	private void initList() {
		for (int i = 0; i < txtOriginal.size(); i++) {
			// 向储存修改后的字符串的列表中添加空成员
			txtTranslated.add("");
		}
	}

	/** 根据返回选择的文件,来进行操作 **/
	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		// TODO Auto-generated method stub
		if (resultCode == Activity.RESULT_OK) {
			Uri uri = data.getData();
			fileSrc = uri.getPath();
			try {
				open(new FileInputStream(fileSrc));
			} catch (IOException e) {
				showMessage(this, e.toString()).show();
			}
		}
		super.onActivityResult(requestCode, resultCode, data);
	}

	/** 返回事件 */
	@Override
	public void onBackPressed() {
		if (isChanged || checkChanged()) { // 保存文件
			showSaveDialog();
		} else {
			finish();
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// 设置主界面布局文件
		setContentView(R.layout.string_list);
		// 初始化列表控件
		stringListView = (ListView) findViewById(R.id.list_res_string);
		// 初始化显示资源类型的文本框
		textCategory = (TextView) findViewById(R.id.textCategory);
		// 为显示资源类型的文本框设置点击事件的监听器
		textCategory.setOnClickListener(MyOnClickListener);
		// 为显示资源类型的文本框设置文本内容改变的监听器
		textCategory.addTextChangedListener(textWatcher);
		// 初始化数据适配器
		mAdapter = new stringListAdapter(this);
		// 为列表控件设置数据适配器
		stringListView.setAdapter(mAdapter);
		this.OpenSystemFile();
	}

	private void open(InputStream resInputStream) {
		// 初始化一个线程用来解析资源文件
		AsyncTask<InputStream, Integer, String> task = new ParseTask();
		try {
			// 开启该线程
			task.execute(resInputStream);
		} catch (OutOfMemoryError e) {
			showMessage(this, getString(R.string.out_of_memory)).show();
		}
		// 初始化一个线程用来获取解析后的资源
		AsyncTask<String, Void, Void> getTask = new GetTask();
		// 开启该线程
		getTask.execute(textCategory.getText().toString());
	}

	public void OpenSystemFile() {
		Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
		intent.setType("*/*");
		intent.addCategory(Intent.CATEGORY_OPENABLE);
		try {
			startActivityForResult(Intent.createChooser(intent, "请选择文件!"), 0x111);
		} catch (android.content.ActivityNotFoundException ex) {
			// Potentially direct the user to the Market with a Dialog
			Toast.makeText(this, "请安装文件管理器", Toast.LENGTH_SHORT).show();
		}
	}

	/**
	 * ELF解析器
	 * 
	 * @param result
	 *            用来存放结果
	 * @param is
	 *            文件输入流
	 **/
	public void parseELF(ResourceCallBack callBack, InputStream is)
			throws UnknownFormatConversionException, IOException {
		elfParser = new Elf(new ByteArrayInputStream(InputStream2ByteArray(is)), callBack);
	}

	/** 显示保存文件的对话框 **/
	private void showSaveDialog() {
		new AlertDialog.Builder(this).setTitle(R.string.notice).setMessage(R.string.ensure_save)
				.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						File file = new File(fileSrc);
						File bak = new File(fileSrc + ".bak");
						file.renameTo(bak);
						file.delete();
						SaveFileTask saveTask = new SaveFileTask();
						saveTask.execute(fileSrc);
					}
				}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						setResult(Activity.RESULT_CANCELED, getIntent());
						finish();
					}
				}).create().show();
	}

	// 保存ELF字符串
	@SuppressLint("DefaultLocale")
	public void writeELFString(String output) throws IOException {
		// 整理RoData
		if (textCategory.getText().toString().equals("rodata")) {
			elfParser.sortStrData(txtOriginal, txtTranslated, elfParser.ro_items);
		} else { // 整理Dynstr
			elfParser.sortStrData(txtOriginal, txtTranslated, elfParser.dy_items);
		}
		OutputStream fos = new FileOutputStream(output);
		elfParser.writeELF(fos);
		fos.close();
	}
}