package com.majeur.applicationsinfo;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.MenuItem;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;

import com.majeur.xmlapkparser.AXMLPrinter;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

/**
 * Activity that shows AndroidManifest.xml of any apps. Application package name must be passed as extra.
 * To correctly display returned xml file, we show it in a {@link android.webkit.WebView}. The way we do that
 * is not very natural, but it's the simplest way to do that.
 * So, asynchronously, we get raw xml string and save it to a file. Then we ask to WebView to display this file,
 * by this way, WebView auto detect xml and display it nicely.
 * File do not need to be kept. We delete it to keep used memory as low as possible, but anyway, each time
 * we will show application's manifest, the same file will be used, so used memory will not grow.
 */
public class ViewManifestActivity extends Activity {

    public static final String EXTRA_PACKAGE_NAME = "package_name";

    private WebView mWebView;
    private ProgressDialog mProgressDialog;

    private String mPath;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getActionBar().setDisplayHomeAsUpEnabled(true);
        mWebView = new WebView(this);
        setContentView(mWebView);

        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setCancelable(false);
        mProgressDialog.setMessage(getString(R.string.loading));

        mPath = getFilesDir() + "/data.xml";
        String packageName = getIntent().getStringExtra(EXTRA_PACKAGE_NAME);

        String filePath = null, applicationLabel = null;
        try {
            filePath = getPackageManager().getPackageInfo(packageName, 0).applicationInfo.sourceDir;
            applicationLabel = getPackageManager().getApplicationInfo(packageName, 0).loadLabel(getPackageManager()).toString();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            Toast.makeText(this, R.string.app_not_installed, Toast.LENGTH_LONG).show();
            finish();
        }

        setTitle(getString(R.string.manifest) + ": " + applicationLabel);
        new AsyncManifestLoader().execute(filePath);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            finish();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void displayContent() {
        WebSettings settings = mWebView.getSettings();
        settings.setBuiltInZoomControls(true);
        settings.setUseWideViewPort(true);
        mWebView.setWebChromeClient(new MyWebChromeClient());
        mWebView.loadUrl(Uri.fromFile(new File(mPath)).toString());
    }

    private void handleError() {
        Toast.makeText(this, R.string.error, Toast.LENGTH_LONG).show();
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        File file = new File(mPath);
        file.delete();
    }

    final class MyWebChromeClient extends WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int progress) {
            if (progress == 100)
                showProgressBar(false);
        }
    }

    private void showProgressBar(boolean show) {
        if (show)
            mProgressDialog.show();
        else
            mProgressDialog.dismiss();
    }

    /**
     * This AsyncTask takes manifest file path as argument
     */
    private class AsyncManifestLoader extends AsyncTask<String, Integer, Boolean> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            showProgressBar(true);
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            String filePath = strings[0];
            String code = getProperXml(AXMLPrinter.getManifestXMLFromAPK(filePath));

            if (code == null) return false;

            try {
                File file = new File(mPath);
                if (!file.exists())
                    file.createNewFile();

                FileOutputStream output = new FileOutputStream(file);
                output.write(code.getBytes());
                output.flush();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return true;
        }

        /**
         * Format xml file to correct indentation ...
         */
        private String getProperXml(String dirtyXml) {
            try {
                Document document = DocumentBuilderFactory.newInstance()
                        .newDocumentBuilder()
                        .parse(new InputSource(new ByteArrayInputStream(dirtyXml.getBytes("utf-8"))));

                XPath xPath = XPathFactory.newInstance().newXPath();
                NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']",
                        document,
                        XPathConstants.NODESET);

                for (int i = 0; i < nodeList.getLength(); ++i) {
                    Node node = nodeList.item(i);
                    node.getParentNode().removeChild(node);
                }

                Transformer transformer = TransformerFactory.newInstance().newTransformer();
                transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
                transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

                StringWriter stringWriter = new StringWriter();
                StreamResult streamResult = new StreamResult(stringWriter);

                transformer.transform(new DOMSource(document), streamResult);

                return stringWriter.toString();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        /**
         * Do not hide progressDialog here, WebView will hide it when content will be displayed
         */
        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
            if (result)
                displayContent();
            else
                handleError();
        }
    }
}