package net.yak.kiwi;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;

import androidx.annotation.NonNull;

import android.os.PersistableBundle;
import android.text.InputType;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridLayout;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.lang.Math;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        radio = new RadioHarness();

        if (savedInstanceState != null) {
            String server = savedInstanceState.getString("server", "");
            long frequency = savedInstanceState.getLong("frequency", 5_000_000);
            RadioHarness.Mode mode = RadioHarness.Mode.unpickle(
                    savedInstanceState.getString("mode", ""));
            radio.setConfig(new RadioHarness.Config(server, frequency, mode, 1000, 12000, true));
        }

        makeMainMenu().start();
    }

    @Override public void onBackPressed() {
        if (actOnBack != null) {
            actOnBack.run();
        } else {
            finish();
        }
    }

    MagicList makeMainMenu() {
        GridLayout grid = new GridLayout(context);
        GridLayout.LayoutParams p = new GridLayout.LayoutParams();
        p.setGravity(Gravity.CENTER_HORIZONTAL);
        grid.setRowCount(2);
        grid.setColumnCount(4);
        for (boolean n : new boolean[]{false, true}) {
            final boolean neg = n;
            final char sign = neg? '-' : '+';
            for (int o : new int[] {100000, 10000, 1000, 100}) {
                final int offset = o;
                Button btn = new Button(context);
                if (o<1000) {
                    btn.setText(String.format("%c%.1f", sign, (double) offset / 1000));
                } else {
                    btn.setText(String.format("%c%d", sign, offset / 1000));
                }
                btn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        RadioHarness.Config cfg = radio.getConfig();
                        cfg.frequency += neg? -offset : offset;
                        radio.setConfig(cfg);
                        radio.restart();
                        makeMainMenu().start();
                    }
                });
                grid.addView(btn);
            }
        }

        TextView info = new TextView(context);
        final RadioHarness h = radio;
        if (h != null) {
            final Map<String,String> sv = h.serverValues;
            if (sv != null) {
                String rx_name = "?";
                String rx_location = "?";
                try {
                    String load_cfg = sv.get("load_cfg");
                    load_cfg = URLDecoder.decode(load_cfg);
                    if (load_cfg != null) {
                        JSONObject obj = new JSONObject(load_cfg);
                        rx_name = URLDecoder.decode("" + obj.get("rx_name"));
                        rx_location = URLDecoder.decode("" + obj.get("rx_location"));
                    }
                    info.setText(rx_name + "\n" + rx_location);
                } catch (Exception ex) {
                            log.error("TextView info: exception=" + ex);
                }
            }
        }

        MagicList z = new MagicList(info, grid);

        z.add(String.format(Locale.ROOT, "Freq: %.3f kHz", (double)radio.getConfig().frequency/ 1000.0), new Runnable() {
            @Override
            public void run() {
                makeFrequencyMenu().start();
            }
        });
        z.add(String.format(Locale.ROOT, "Mode: %s", radio.getConfig().mode.toString()), new Runnable() {
            @Override
            public void run() {
                makeModeMenu().start();
            }
        });
        z.add(String.format(Locale.ROOT, "Server: %s", radio.getConfig().server), new Runnable() {
            @Override
            public void run() {
                makeServerMenu().start();
            }
        });
        z.add(String.format(Locale.ROOT, "Geek Menu"), new Runnable() {
            @Override
            public void run() {
                makeGeekMenu().start();
            }
        });

        z.setOnBack(null);  // finish
        return z;
    }


    MagicList makeCompressMenu() {
        MagicList z = new MagicList();

        z.add(String.format(Locale.ROOT, "Compression: On", radio.getConfig().compress), new Runnable() {
            @Override
            public void run() {
                RadioHarness.Config cfg = radio.getConfig();
                cfg.compress = true;
                radio.setConfig(cfg);
                radio.start();
                makeGeekMenu().start();
            }
        });
        z.add(String.format(Locale.ROOT, "Compression: off", radio.getConfig().compress), new Runnable() {
            @Override
            public void run() {
                RadioHarness.Config cfg = radio.getConfig();
                cfg.compress = false;
                radio.setConfig(cfg);
                radio.start();
                makeGeekMenu().start();
            }
        });

        z.backGeek();
        return z;
    }

    MagicList makeGeekMenu() {
        MagicList z = new MagicList();
        final RadioHarness h = radio;

        z.add(String.format(Locale.ROOT, "Compression: %s", radio.getConfig().compress? "On" : "Off"), new Runnable() {
            @Override
            public void run() {
                makeCompressMenu().start();
            }
        });
        z.add(String.format(Locale.ROOT, "Rate: %s", radio.getConfig().rate), new Runnable() {
            @Override
            public void run() {
                makeRateMenu().start();
            }
        });
        z.add(String.format(Locale.ROOT, "AF Gain: × %.2f", (double)radio.getConfig().gain / 1000.0), new Runnable() {
            @Override
            public void run() {
                makeGainMenu().start();
            }
        });
        if (h != null) {
            final Map<String,String> sv = h.serverValues;
            if (sv != null) {

                z.add("View server params", new Runnable() {
                    @Override
                    public void run() {
                        setContentView(makeShowServerParams());
                    }
                });
            }
        }

        return z;
    }

    View makeShowServerParams() {
        ArrayList<String> labels = new ArrayList<>();
        Map<String,String> serverValues = null;
        RadioHarness h;
        h = radio;
        if (h != null) {
            serverValues = h.serverValues;
        }
        if (serverValues != null) {
            ArrayList<String> keys = new ArrayList<>();
            for (String key : serverValues.keySet()) {
                keys.add(key);
            }
            Collections.sort(keys);

            for (String key : keys) {
                if (key.equals("load_cfg")) {
                    String load_cfg = serverValues.get(key);
                    load_cfg = URLDecoder.decode(load_cfg);
                    try {
                        JSONObject obj = new JSONObject(load_cfg);
                        Iterator<String> it = obj.keys();
                        while (it.hasNext()) {
                            String jkey = it.next();
                            Object j2 = obj.get(jkey);
                            if (j2.getClass().equals(JSONObject.class)) {
                                JSONObject obj2 = (JSONObject)j2;
                                Iterator<String> it2 = obj2.keys();
                                while (it2.hasNext()) {
                                    String jkey2 = it2.next();
                                    Object j3 = obj2.get(jkey2);
                                    labels.add("load_cfg." + jkey + "." + jkey2 + " = " + j3.toString());
                                }
                            } else {
                                labels.add("load_cfg." + jkey + " = " + URLDecoder.decode(obj.get(jkey).toString()));
                            }
                        }
                    } catch (JSONException ex) {
                        log.error("load_cfg: JSONException: " + ex);
                    }
                    continue;
                }
                labels.add(key + " = " + serverValues.get(key));
            }

        }
        actOnBack = new Runnable() {
            @Override
            public void run() {
                makeGeekMenu().start();
            }
        };

        ListView listView = new ListView(context);
        listView.setAdapter(new ArrayAdapter<String>(context, R.layout.list_item, labels));
        return listView;
    }

    MagicList makeRateMenu() {
        MagicList z = new MagicList();
        for (int i : new int[]{ 8000, 12000, 24000, 48000}) {
            final int rate = i;
            String label =  (rate==12000)? "Best: 12000" : "Crude Resample: " + rate;
            z.add(label, new Runnable() {
                @Override
                public void run() {
                    RadioHarness.Config cfg = radio.getConfig();
                    cfg.rate = rate;
                    radio.setConfig(cfg);
                    radio.start();
                    makeGeekMenu().start();
                }
            });
        }
        z.backGeek();
        return z;
    }

    MagicList makeGainMenu() {
        MagicList z = new MagicList();
        for (int db = -5; db <= 10; db++) {
            double g = Math.pow(10.0, (double)db / 10.0);
            final int gain = (int)(g * 1000);
            String label = String.format("AF Gain: %d db (× %.2f)", db, g);
            z.add(label, new Runnable() {
                @Override
                public void run() {
                    RadioHarness.Config cfg = radio.getConfig();
                    cfg.gain = gain;
                    radio.setConfig(cfg);
                    radio.start();
                    makeGeekMenu().start();
                }
            });
        }
        z.backGeek();
        return z;
    }

    MagicList makeServerMenu() {
        MagicList z = new MagicList();

        z.add("(none)", new Runnable() {
            @Override
            public void run() {
                RadioHarness.Config cfg = radio.getConfig();
                cfg.server = "";
                radio.setConfig(cfg);
                radio.start();
                makeMainMenu().start();
            }
        });

        for (String e : Predefined.Servers) {
            final String server = e;
            z.add(server, new Runnable() {
                @Override
                public void run() {
                    RadioHarness.Config cfg = radio.getConfig();
                    cfg.server = server;
                    radio.setConfig(cfg);
                    radio.start();
                    makeMainMenu().start();
                }
            });
        }
        return z;
    }

    MagicList makeFrequencyMenu() {
        MagicList z = new MagicList();

        z.add("-> Type your own frequency <-", new Runnable() {
            @Override
            public void run() {
                View view = makeEnterFreqView();
                actOnBack = new Runnable() {
                    @Override
                    public void run() {
                        makeMainMenu().start();
                    }
                };
                setContentView(view);
            }
        });

        for (long e : Predefined.Frequencies) {
            final long frequency = e;
            z.add(String.format(Locale.ROOT, "%.3f", (double)frequency/1000.0), new Runnable() {
                @Override
                public void run() {
                    RadioHarness.Config cfg = radio.getConfig();
                    cfg.frequency = frequency;
                    radio.setConfig(cfg);
                    radio.restart();
                    makeMainMenu().start();
                }
            });
        }
        return z;
    }

    MagicList makeModeMenu() {
        MagicList z = new MagicList();
        for (RadioHarness.Mode e : RadioHarness.Mode.values()) {
            final RadioHarness.Mode mode = e;
            z.add(e.toString(), new Runnable() {
                @Override
                public void run() {
                    RadioHarness.Config cfg = radio.getConfig();
                    cfg.mode = mode;
                    radio.setConfig(cfg);
                    radio.restart();
                    makeMainMenu().start();
                }
            });
        }
        return z;
    }

    class MagicList {
        private ArrayList<String> labels = new ArrayList<>();
        private ArrayList<Runnable> actions = new ArrayList<>();
        private View[] prefixes;

        // Default backAction is to return to the main menu.
        private Runnable backAction = new Runnable() {
            @Override
            public void run() {
                makeMainMenu().start();
            }
        };

        public MagicList(View... prefixes) {
            this.prefixes = prefixes;
        }

        public void add(String label, Runnable action) {
            labels.add(label);
            actions.add(action);
        }
        public void setOnBack(Runnable r) {
            backAction = r;
        }
        public void backGeek() {
            backAction = new Runnable() {
                @Override
                public void run() {
                    makeGeekMenu().start();
                }
            };
        }
        void start() {
            TextView title = new TextView(context);
            title.setText("Ham Listen for KiwiSDR");
            title.setGravity(1 /*center_horizontal*/);

            ListView listView = new ListView(context);
            listView.setAdapter(new ArrayAdapter<String>(context, R.layout.list_item, labels));
            listView.setLayoutParams(new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.FILL_PARENT, 1.0f));
            listView.setTextFilterEnabled(true);
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                    Toast.makeText(context, labels.get(i), Toast.LENGTH_SHORT).show();
                    actions.get(i).run();
                }
            });

            LinearLayout linear = new LinearLayout(context);
            linear.setOrientation(LinearLayout.VERTICAL);
            linear.addView(title);
            for (View p : prefixes) {
                linear.addView(p);
            }
            linear.addView(listView);
            actOnBack = backAction;
            setContentView(linear);
        }
    }

    View makeEnterFreqView() {
        final EditText ed = new EditText(context);
        ed.setText(String.format(Locale.ROOT, "%.3f",  (double)(editedFreq / 1000.0)));
        ed.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
        ed.setLayoutParams(fillingWidgetParams);
        ///ed.setTextAppearance(this, R.style.teletype);
        ed.setBackgroundColor(Color.LTGRAY);
        ed.setGravity(Gravity.TOP);

        Button btn = new Button(this);
        btn.setText("Save");
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String text = ed.getText().toString();
                try {
                    double kHz = Double.parseDouble(text);
                    editedFreq = (long)(kHz * 1000 + 0.5);
                } catch (NumberFormatException ex) {
                    log.error("NumberFormatException: " + ex);
                }

                RadioHarness.Config cfg = radio.getConfig();
                cfg.frequency = editedFreq;
                radio.setConfig(cfg);
                radio.start();
                makeMainMenu().start();
            }
        });

        TextView textView = new TextView(context);
        textView.setText("Enter frequency in kHz.");

        LinearLayout linear = new LinearLayout(context);
        linear.setOrientation(LinearLayout.VERTICAL);
        linear.addView(textView);
        linear.addView(btn);
        linear.addView(ed);
        return linear;
    }

    @Override
    protected void onStart() {
        super.onStart();
        log.info("onStart");
    }

    @Override
    protected void onStop() {
        super.onStop();
        log.info("onStop");
    }

    @Override
    protected void onResume() {
        super.onResume();
        log.info("onResume");
        radio.setConfig(new RadioHarness.Config(
                "sybil.yak.net", 5_000_000, RadioHarness.Mode.AM, 1000, 12000, true));
    }

    @Override
    protected void onPause() {
        super.onPause();
        log.info("onPause");
        radio.stop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        log.info("onDestroy");
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        // If phone is rotated, don't restart Activity, just go back to main menu.
        super.onConfigurationChanged(newConfig);
        makeMainMenu().start();
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        outState.putString("server", radio.getConfig().server);
        outState.putLong("frequency", radio.getConfig().frequency);
        outState.putString("mode", radio.getConfig().mode.pickle());
    }

    private RadioHarness radio = null;
    private final Context context = this;
    private Logger log = LoggerFactory.getLogger(MainActivity.class);
    private Runnable actOnBack = null;
    private long editedFreq = 0;

    private final LinearLayout.LayoutParams fillingWidgetParams = new LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.FILL_PARENT,
            ViewGroup.LayoutParams.FILL_PARENT, 1.0f);
}
