package cloud.hmml.mmw;

import android.app.DownloadManager;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.ion.Ion;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;

import net.arnx.jsonic.JSON;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.ExecutionException;

public class ThemeDownloadActivity extends AppCompatActivity {

    static class RemoteThemeHolder {
        public RemoteTheme[] themes = null;
        public int current_page = 0;
        public int total_pages = 0;
        public int total_count = 0;
        public int per_page = 0;
        public boolean is_last_page = true;
        public boolean is_first_page = true;
        public ArrayList<RemoteTheme> getThemes() {
            if (themes == null)
                return null;
            return new ArrayList(Arrays.asList(themes));
        }
    }

    static class RemoteTheme {
        public int id;
        public String archive_url;
        public String archive_digest;
        public String name;
        public String author;
        public String url;
        public String short_desc;
        public String preview1;
        public String preview2;
        public String preview3;
        public String preview4;
        public boolean download_in_progress = false;
        public boolean is_stored = false;
        public boolean update_avaiable = false;
        public int revision = 1;

        public boolean checkDigest(File file) {
            FileInputStream is = null;
            try {
                is = new FileInputStream(file);
                return checkDigest(is);
            } catch (FileNotFoundException e) {
                return false;
            } finally {
                try {
                    if (is != null)
                        is.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        public boolean checkDigest(InputStream is){
            byte[] buffer = new byte[64 * 1024];
            int readbytes = 0;
            try {
                MessageDigest digest = MessageDigest.getInstance("MD5");
                do {
                    readbytes = is.read(buffer);
                    if (readbytes > 0)
                        digest.update(buffer, 0, readbytes);
                } while (readbytes > 0);
                StringBuffer hexdigest = new StringBuffer();
                for (byte b: digest.digest()) {
                    hexdigest.append(String.format("%02x", b & 0xFF));
                }
                Log.d("theme-downloader", "Target file hex digest: "+hexdigest.toString() + ", expected: "+ archive_digest);
                if (!hexdigest.toString().toLowerCase().equals(archive_digest.toLowerCase()))
                    return false;
                Log.d("theme-downloader", "Digest matched.");
                return true;
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return false;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
    }

    private SearchView mSearchView;
    private ListView listView;
    private RemoteThemeAdapter adapter;

    static class RemoteThemeLoader extends AsyncTask<RemoteThemeAdapter, Void, RemoteThemeHolder> {
        private RemoteThemeAdapter adapter;

        @Override
        protected RemoteThemeHolder doInBackground(RemoteThemeAdapter... params) {
            this.adapter = params[0];
            RemoteThemeHolder holder = adapter.getJson();
            if (holder.themes != null) {
                for (RemoteTheme t: holder.themes) {
                    t.is_stored = Theme.exists(adapter.getContext(), Theme.TYPE_IN_STORAGE,  t.id);
                    if (t.is_stored) {
                        Theme st = Theme.load(adapter.getContext(), Theme.TYPE_IN_STORAGE, t.id);
                        if (st != null)
                            t.update_avaiable = st.revision < t.revision;
                    }
                }
            }
            return holder;
        }

        @Override
        protected void onPostExecute(RemoteThemeHolder themeHolder) {
            if (isCancelled())
                return;
            super.onPostExecute(themeHolder);
            adapter.cur_page = themeHolder.current_page;
            adapter.more_count = themeHolder.total_count - themeHolder.per_page * themeHolder.current_page;
            adapter.next_page = themeHolder.is_last_page ? -1 : themeHolder.current_page + 1;
            adapter.updateMoreText();
            if (themeHolder.themes == null)
                return;
            adapter.addAll(themeHolder.getThemes());
        }
    }

    static class RemoteThemeAdapter extends ArrayAdapter {
        static final String BASE_URL = "https://mmw.hmml.cloud/";
        private final ProgressDialog progressDialog;
        String query;
        int cur_page = 0;
        int next_page = 0;
        int more_count = 0;
        private WeakReference<View> moreView = null;
        private RemoteThemeLoader curLoader = null;

        public RemoteThemeAdapter(@NonNull Context context, @LayoutRes int resource) {
            super(context, resource);
            progressDialog = new ProgressDialog(context);
            progressDialog.setCancelable(false);
            progressDialog.setIndeterminate(true);
            progressDialog.setMessage(context.getText(R.string.loading_progress));
        }

        public boolean isLastPage() {
            return next_page < 0;
        }

        public String getQuery() {
            return query;
        }

        public void setQuery(String query) {
            this.query = query;
        }

        public void refresh() {
            if (curLoader != null)
                curLoader.cancel(true);
            clear();
            cur_page = 0;
            next_page = 0;
            more_count = 0;
            curLoader = new RemoteThemeLoader();
            curLoader.execute(this);
        }

        public void loadNextPage() {
            if (isLastPage())
                return;
            if (curLoader != null)
                curLoader.cancel(true);
            curLoader = new RemoteThemeLoader();
            curLoader.execute(this);
        }

        public void updateMoreText() {
            if (moreView == null || moreView.get() == null)
                return;
            String text = more_count > 1 ?
                    String.format(Locale.US, getContext().getString(R.string.more_n_items), more_count) :
                    getContext().getString(R.string.no_more_items);

            ((TextView) moreView.get().findViewById(R.id.more_txt)).setText(text);
        }

        public void setMoreView(View v) {
            this.moreView = new WeakReference<View>(v);
        }

        @NonNull
        @Override
        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
            if (convertView == null) {
                convertView = LayoutInflater.from(getContext()).inflate(R.layout.theme_item, parent, false);
            }

            RemoteTheme theme = (RemoteTheme) getItem(position);

            ImageView image1  = (ImageView) convertView.findViewById(R.id.theme_image1);
            ImageView image2  = (ImageView) convertView.findViewById(R.id.theme_image2);
            ImageView image3  = (ImageView) convertView.findViewById(R.id.theme_image3);
            ImageView image4  = (ImageView) convertView.findViewById(R.id.theme_image4);
            TextView name     = (TextView)  convertView.findViewById(R.id.theme_name);
            TextView author   = (TextView)  convertView.findViewById(R.id.theme_author);
            TextView desc     = (TextView)  convertView.findViewById(R.id.theme_desc);
            TextView badge_dl = (TextView)  convertView.findViewById(R.id.badge_downloading);
            TextView badge_up = (TextView)  convertView.findViewById(R.id.badge_update);
            TextView badge_ok = (TextView)  convertView.findViewById(R.id.badge_downloaded);


            ImageLoader imageloader = ImageLoader.getInstance();
            DisplayImageOptions options = new DisplayImageOptions.Builder()
                    .cacheOnDisk(true)
                    .build();
            image1.setImageDrawable(null);
            image2.setImageDrawable(null);
            image3.setImageDrawable(null);
            image4.setImageDrawable(null);
            imageloader.displayImage(theme.preview1, image1, options);
            imageloader.displayImage(theme.preview2, image2, options);
            imageloader.displayImage(theme.preview3, image3, options);
            imageloader.displayImage(theme.preview4, image4, options);

            name.setText(theme.name);
            author.setText(theme.author);
            desc.setText(theme.short_desc);

            badge_ok.setVisibility(theme.is_stored ? View.VISIBLE : View.GONE);
            badge_up.setVisibility(theme.update_avaiable ? View.VISIBLE : View.GONE);
            badge_dl.setVisibility(theme.download_in_progress ? View.VISIBLE : View.GONE);

            return convertView;
        }

        private RemoteThemeHolder getJson() {
            final Handler uiHandler = new Handler(Looper.getMainLooper());
            try {
                uiHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        progressDialog.show();
                    }
                });
                InputStream is = Ion.with(getContext())
                        .load(BASE_URL + "themes.json")
                        .addQuery("q", getQuery())
                        .addQuery("page", new Integer(next_page).toString())
                        .setHeader("X-Requested-With", "Android cloud.hmml.mmw")
                        .progressDialog(progressDialog)
                        .asInputStream()
                        .setCallback(new FutureCallback<InputStream>() {
                            @Override
                            public void onCompleted(Exception e, InputStream result) {
                                uiHandler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        progressDialog.cancel();
                                    }
                                });
                            }
                        })
                        .get();
                return JSON.decode(is, RemoteThemeHolder.class);
            } catch (InterruptedException e) {
                Log.d("download-adapter", "Inttrupted in fetching json: " + e.getMessage());
                progressDialog.cancel();
                return null;
            } catch (ExecutionException e) {
                Log.e("download-adapter", "ExecutionException in fetching json: "+e.getMessage());
                e.printStackTrace();
                progressDialog.cancel();
                uiHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getContext(), R.string.load_failed, Toast.LENGTH_LONG).show();
                    }
                });
                return null;
            } catch (IOException e) {
                Log.e("download-adapter", "IOException in fetching json: "+e.getMessage());
                e.printStackTrace();
                progressDialog.cancel();
                uiHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getContext(), R.string.load_failed, Toast.LENGTH_LONG).show();
                    }
                });
                return null;
            } catch (net.arnx.jsonic.JSONException e) {
                Log.e("download-adapter", "JSONException in fetching json: "+e.getMessage());
                e.printStackTrace();
                progressDialog.cancel();
                uiHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getContext(), R.string.load_failed, Toast.LENGTH_LONG).show();
                    }
                });
                return null;
            }
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_theme_download);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        adapter = new RemoteThemeAdapter(this, R.layout.theme_item);

        View footerView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.theme_list_footer, null, false);
        adapter.setMoreView(footerView);


        listView = (ListView) findViewById(R.id.download_theme_list);
        listView.setAdapter(adapter);
        listView.addFooterView(footerView);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (position >= adapter.getCount()) { // More clicked
                    adapter.loadNextPage();
                    return;
                }

                final RemoteTheme rtheme = (RemoteTheme) parent.getItemAtPosition(position);

                if (rtheme == null)
                    return;

                if (rtheme.download_in_progress || rtheme.is_stored && !rtheme.update_avaiable)
                    return;

                if (getApplicationContext().getExternalCacheDir() == null) {
                    Toast.makeText(getApplicationContext(), "Please insert SD card", Toast.LENGTH_LONG).show();
                    return;
                }
                File file = new File(getApplicationContext().getExternalCacheDir(), "/theme-" + rtheme.id + ".zip");
                Log.d("td", "path:" + file.getPath());
                (new File(file.getParent())).mkdirs();

                DownloadManager.Request request = new DownloadManager.Request(Uri.parse(rtheme.archive_url));
                request.setTitle(rtheme.name);
                request.setMimeType("application/zip");
                request.setVisibleInDownloadsUi(false);
                request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
                request.setDestinationUri(Uri.fromFile(file));

                rtheme.download_in_progress = true;
                adapter.notifyDataSetChanged();

                final DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                final long queueId = downloadManager.enqueue(request);

                final BroadcastReceiver reciever = new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        String action = intent.getAction();
                        if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
                            long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
                            DownloadManager.Query query = new DownloadManager.Query();
                            if (queueId != downloadId)
                                return;
                            query.setFilterById(downloadId);
                            Cursor c = downloadManager.query(query);

                            try {
                                if (!c.moveToFirst())
                                    return;
                                String targetUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));

                                if (DownloadManager.STATUS_SUCCESSFUL != c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
                                    Log.e("theme-downloader", "Download status is not successfull: " + targetUri);
                                    return;
                                }

                                Log.d("theme-downloader", "download complete:" + targetUri);
                                Uri dlUri = downloadManager.getUriForDownloadedFile(downloadId);

                                try {
                                    if (!rtheme.checkDigest(context.getContentResolver().openInputStream(dlUri))) {
                                        Log.e("theme-downloader", "Digest missmatch! Rejecting file: "+dlUri);
                                        return;
                                    }
                                } catch (FileNotFoundException e) {
                                    return;
                                }
                                Theme.create(context, dlUri);
                                rtheme.is_stored = true;
                            } finally {
                                rtheme.download_in_progress = false;
                                adapter.notifyDataSetChanged();
                                c.close();
                                context.unregisterReceiver(this);
                                downloadManager.remove(downloadId);
                            }
                        }
                    }
                };
                getApplicationContext().registerReceiver(reciever, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
            }
        });


        adapter.refresh();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Set Menu
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_search, menu);

        MenuItem menuItem = menu.findItem(R.id.toolbar_menu_search);

        mSearchView = (SearchView) MenuItemCompat.getActionView(menuItem);

        // whether display Magnifying Glass Icon at first
        mSearchView.setIconifiedByDefault(true);

        // whether display Submit Button
        mSearchView.setSubmitButtonEnabled(false);

        mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                adapter.setQuery(query);
                adapter.refresh();
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                if (newText == null || newText.equals("")) {
                    adapter.setQuery(null);
                    adapter.refresh();
                }
                return true;
            }
        });

        return super.onCreateOptionsMenu(menu);
    }
}
