/*
 * Copyright [2020] [Doric.Pub]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package pub.doric.devkit.ui;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Base64;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import pub.doric.Doric;
import pub.doric.DoricContext;
import pub.doric.DoricContextManager;
import pub.doric.devkit.DoricDebugDriver;
import pub.doric.devkit.DoricDev;
import pub.doric.devkit.R;
import pub.doric.devkit.qrcode.DisplayUtil;
import pub.doric.devkit.qrcode.activity.CaptureActivity;
import pub.doric.devkit.qrcode.activity.CodeUtils;

import static pub.doric.devkit.ui.DoricShowNodeTreeActivity.DORIC_CONTEXT_ID_KEY;

public class DoricDevActivity extends AppCompatActivity implements DoricDev.StatusCallback {
    private int REQUEST_CODE = 100;
    private ContextCellAdapter cellAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DisplayUtil.initDisplayOpinion(getApplicationContext());
        setContentView(R.layout.layout_debug_context);
        initHeaders();
        initList();
    }

    private void initList() {
        RecyclerView recyclerView = findViewById(R.id.list);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        this.cellAdapter = new ContextCellAdapter();
        recyclerView.setAdapter(this.cellAdapter);
    }

    @Override
    protected void onStart() {
        super.onStart();
        DoricDev.getInstance().addStatusCallback(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        DoricDev.getInstance().removeStatusCallback(this);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1) {
            for (int i = 0; i < permissions.length; i++) {
                if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                    Intent intent = new Intent(DoricDevActivity.this, CaptureActivity.class);
                    startActivityForResult(intent, REQUEST_CODE);
                }
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (null != data) {
            Bundle bundle = data.getExtras();
            if (bundle == null) {
                return;
            }
            if (bundle.getInt(CodeUtils.RESULT_TYPE) == CodeUtils.RESULT_SUCCESS) {
                String result = bundle.getString(CodeUtils.RESULT_STRING);
                DoricDev.getInstance().connectDevKit("ws://" + result + ":7777");
            }
        }
    }


    private void initHeaders() {
        TextView tvConnection = findViewById(R.id.tv_connect_info);
        TextView tvInput = findViewById(R.id.tv_input);
        TextView tvScan = findViewById(R.id.tv_scan);
        TextView tvDisconnect = findViewById(R.id.tv_disconnect);
        if (DoricDev.getInstance().isInDevMode()) {
            tvConnection.setText(DoricDev.getInstance().getIP());
            tvInput.setVisibility(View.GONE);
            tvScan.setVisibility(View.GONE);
            tvDisconnect.setVisibility(View.VISIBLE);
            tvConnection.setBackgroundColor(Color.parseColor("#2ed573"));
        } else {
            tvConnection.setText("Disconnected");
            tvDisconnect.setVisibility(View.GONE);
            tvInput.setVisibility(View.VISIBLE);
            tvScan.setVisibility(View.VISIBLE);
            tvConnection.setBackgroundColor(Color.parseColor("#a4b0be"));
        }

        tvScan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (DoricDev.getInstance().isRunningInEmulator) {
                    DoricDev.getInstance().connectDevKit("ws://" + "10.0.2.2" + ":7777");
                } else {
                    if (ContextCompat.checkSelfPermission(DoricDevActivity.this, Manifest.permission.CAMERA)
                            != PackageManager.PERMISSION_GRANTED) {
                        if (ActivityCompat.shouldShowRequestPermissionRationale(DoricDevActivity.this,
                                Manifest.permission.CAMERA)) {
                        } else {
                            ActivityCompat.requestPermissions(DoricDevActivity.this,
                                    new String[]{Manifest.permission.CAMERA,}, 1);
                        }
                    } else {
                        Intent intent = new Intent(DoricDevActivity.this, CaptureActivity.class);
                        startActivityForResult(intent, REQUEST_CODE);
                    }
                }
            }
        });

        tvInput.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View clickView) {
                if (DoricDev.getInstance().isRunningInEmulator) {
                    DoricDev.getInstance().connectDevKit("ws://" + "10.0.2.2" + ":7777");
                } else {
                    AlertDialog.Builder builder = new AlertDialog.Builder(DoricDevActivity.this, R.style.Theme_Doric_Modal_Prompt);
                    builder.setTitle("Please input devkit ip");
                    View v = LayoutInflater.from(DoricDevActivity.this).inflate(R.layout.doric_modal_prompt, null);
                    final EditText editText = v.findViewById(R.id.edit_input);
                    editText.setHint("192.168.1.1");
                    String ip = DoricDev.getInstance().getIP();
                    if (!TextUtils.isEmpty(ip)) {
                        editText.setText(ip);
                        editText.setSelection(ip.length());
                    }
                    builder.setView(v);
                    builder
                            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String ip = editText.getText().toString();
                                    DoricDev.getInstance().connectDevKit("ws://" + ip + ":7777");
                                }
                            })
                            .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                }
                            });
                    builder.show();
                }
            }
        });
        tvDisconnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DoricDev.getInstance().closeDevMode();
            }
        });
        SwitchCompat snapshotSwitch = findViewById(R.id.switch_snapshot);
        snapshotSwitch.setChecked(Doric.isEnableRenderSnapshot());
        snapshotSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Doric.enableRenderSnapshot(isChecked);
            }
        });
        setSwitch(snapshotSwitch);

        SwitchCompat profileSwitch = findViewById(R.id.switch_profile);
        profileSwitch.setChecked(Doric.isEnablePerformance());
        profileSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Doric.enablePerformance(isChecked);
            }
        });
        setSwitch(profileSwitch);
    }

    private void setSwitch(SwitchCompat switchCompat) {
        ColorStateList trackTintList = new ColorStateList(new int[][]{
                new int[]{android.R.attr.state_checked}, new int[]{}},
                new int[]{Color.parseColor("#2ed573"), Color.parseColor("#a4b0be")});
        switchCompat.setTrackTintList(trackTintList);

        ColorStateList thumbTintList = new ColorStateList(new int[][]{
                new int[]{android.R.attr.state_checked}, new int[]{}},
                new int[]{Color.WHITE, Color.WHITE});
        switchCompat.setThumbTintList(thumbTintList);
    }


    @Override
    public void onOpen(String url) {
        initHeaders();
        this.cellAdapter.notifyDataSetChanged();
    }

    @Override
    public void onClose(String url) {
        initHeaders();
        this.cellAdapter.notifyDataSetChanged();
    }

    @Override
    public void onFailure(Throwable throwable) {
        initHeaders();
        this.cellAdapter.notifyDataSetChanged();
    }

    @Override
    public void onReload(DoricContext context, String script) {
        this.cellAdapter.notifyDataSetChanged();
    }

    @Override
    public void onStartDebugging(DoricContext context) {
        this.cellAdapter.notifyDataSetChanged();
    }

    @Override
    public void onStopDebugging() {
        this.cellAdapter.notifyDataSetChanged();
    }

    private static class ContextCellHolder extends RecyclerView.ViewHolder {
        public TextView tvId;
        public TextView tvSource;
        public ImageView ivDebug;
        public View layoutBtn;

        public ContextCellHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

    private static String toHex(byte[] bytes) {
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        char[] resultCharArray = new char[bytes.length * 2];
        int index = 0;
        for (byte b : bytes) {
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        return new String(resultCharArray);
    }

    private static String md5(String s) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(s.getBytes(Charset.forName("UTF-8")));
            return toHex(bytes);
        } catch (Exception e) {
            return "";
        }
    }

    private static class ContextCellAdapter extends RecyclerView.Adapter<ContextCellHolder> {
        private final ArrayList<DoricContext> contexts = new ArrayList<>();
        private Bitmap icon_off;
        private Bitmap icon_on;
        private String debug_off = " ";
        private String debug_on = " ";

        private ContextCellAdapter() {
            this.contexts.addAll(DoricContextManager.aliveContexts());
            this.icon_off = getIcon(debug_off);
            this.icon_on = getIcon(debug_on);
        }

        private Bitmap getIcon(String str) {
            byte[] data = Base64.decode(str, Base64.DEFAULT);
            return BitmapFactory.decodeByteArray(data, 0, data.length);
        }

        @NonNull
        @Override
        public ContextCellHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View cell = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_debug_context_cell, parent, false);
            ContextCellHolder cellHolder = new ContextCellHolder(cell);
            cellHolder.tvId = cell.findViewById(R.id.context_id_text_view);
            cellHolder.tvSource = cell.findViewById(R.id.source_text_view);
            cellHolder.ivDebug = cell.findViewById(R.id.icon_debug);
            cellHolder.layoutBtn = cell.findViewById(R.id.layout_btn);
            return cellHolder;
        }

        @Override
        public void onBindViewHolder(@NonNull final ContextCellHolder holder, int position) {
            final DoricContext context = contexts.get(position);
            holder.tvId.setText(context.getContextId());
            holder.tvSource.setText(context.getSource());
            holder.ivDebug.setImageBitmap(DoricDev.getInstance().isInDevMode() ? icon_on : icon_off);
            holder.itemView.setBackgroundColor(position % 2 == 0 ? Color.parseColor("#ecf0f1") : Color.parseColor("#bdc3c7"));
            if (DoricDev.getInstance().isReloadingContext(context)) {
                holder.itemView.setBackgroundColor(Color.parseColor("#ffeaa7"));
            }
            if (context.getDriver() instanceof DoricDebugDriver) {
                holder.itemView.setBackgroundColor(Color.parseColor("#fab1a0"));
            }
            holder.layoutBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(final View v) {
                    final Map<String, DialogInterface.OnClickListener> actionMap = new HashMap<>();

                    actionMap.put("View source", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            AlertDialog.Builder builder = new AlertDialog.Builder(holder.itemView.getContext(), R.style.Theme_Doric_Modal_Alert);
                            builder.setTitle(String.format(Locale.getDefault(),
                                    "View source: %s",
                                    context.getSource()));
                            String btnTitle = holder.itemView.getContext().getString(android.R.string.ok);
                            builder.setMessage(
                                    String.format(Locale.getDefault(),
                                            "Size:%d\nMD5:%s\nScript:\n%s",
                                            context.getScript().length(),
                                            md5(context.getScript()),
                                            context.getScript()))
                                    .setPositiveButton(btnTitle, new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            dialog.dismiss();
                                        }
                                    });
                            builder.setCancelable(false);
                            builder.show();
                        }
                    });
                    if (DoricDev.getInstance().isInDevMode()) {
                        if (context.getDriver() instanceof DoricDebugDriver) {
                            actionMap.put("Stop debugging", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    DoricDev.getInstance().stopDebugging(true);
                                }
                            });
                        } else {
                            actionMap.put("Start debugging", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    DoricDev.getInstance().requestDebugging(context);
                                }
                            });
                        }
                    }
                    if (Doric.isEnableRenderSnapshot()) {
                        actionMap.put("Snapshot", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Activity activity = (Activity) context.getContext();
                                ViewGroup view = (ViewGroup) activity.getWindow().getDecorView();
                                DoricSnapshotView doricSnapshotView = new DoricSnapshotView(activity, context);
                                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                                layoutParams.topMargin = 200;
                                view.addView(doricSnapshotView, layoutParams);
                                ((Activity) v.getContext()).finish();
                            }
                        });
                    }
                    if (Doric.isEnablePerformance()) {
                        actionMap.put("Performance", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent intent = new Intent(holder.itemView.getContext(), DoricDevPerfActivity.class);
                                intent.putExtra(DORIC_CONTEXT_ID_KEY, context.getContextId());
                                v.getContext().startActivity(intent);
                            }
                        });
                    }

                    actionMap.put("View node tree", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(holder.itemView.getContext(), DoricShowNodeTreeActivity.class);
                            intent.putExtra(DORIC_CONTEXT_ID_KEY, context.getContextId());
                            v.getContext().startActivity(intent);
                        }
                    });
                    final String[] items = actionMap.keySet().toArray(new String[0]);
                    AlertDialog.Builder builder = new AlertDialog.Builder(holder.itemView.getContext(), R.style.Theme_Doric_Modal);
                    builder.setTitle(String.format("%s %s", context.getContextId(), context.getSource()));
                    builder.setIcon(new BitmapDrawable(holder.itemView.getContext().getResources(), icon_on));
                    builder.setItems(items, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            String title = items[which];
                            DialogInterface.OnClickListener listener = actionMap.get(title);
                            if (listener != null) {
                                listener.onClick(dialog, which);
                            }
                            dialog.dismiss();
                        }
                    });
                    builder.create().show();
                }
            });
        }

        @Override
        public int getItemCount() {
            return this.contexts.size();
        }
    }
}
