/**
 * *************************************************************************
 * Copyright (C) 2009 by Paul Lutus * lutusp@arachnoid.com * * This program is
 * free software; you can redistribute it and/or modify * it under the terms of
 * the GNU General Public License as published by * the Free Software
 * Foundation; either version 2 of the License, or * (at your option) any later
 * version. * * This program is distributed in the hope that it will be useful,
 * * but WITHOUT ANY WARRANTY; without even the implied warranty of *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General
 * Public License for more details. * * You should have received a copy of the
 * GNU General Public License * along with this program; if not, write to the *
 * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA. *
 * *************************************************************************
 */
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tankcalc;

import java.awt.*;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.*;
import javax.swing.*;

/**
 *
 * @author lutusp
 */
final public class InitManager {

    // these two values need to be changed
    // for each aplication of this class
    TankCalc parent;
    TankProperties source;

    String fileSep;
    String lineSep;
    String appName;
    String userDir;
    String userPath;
    String initPath;
    String defaultValues = "";

    public InitManager(TankCalc p, boolean isApplet) {
        parent = p;
        setSource();
        getDefaultValues();
        lineSep = System.getProperty("line.separator");
        fileSep = System.getProperty("file.separator");
        if (!isApplet) {
            appName = parent.getClass().getSimpleName();
            userDir = System.getProperty("user.home");
            userPath = userDir + fileSep + "." + appName;
            initPath = userPath + fileSep + appName + ".ini";
            testMakeDirs(userPath);
        }
    }

    void setSource() {
        source = parent.sharedTP;
    }

    private String[] parsePair(String val) {
        String pair[] = null;
        int p = val.indexOf("=");
        if (p > 0) {
            pair = new String[]{val.substring(0, p), val.substring(p + 1, val.length())};
            for (int i = 0; i < 2 && i < pair.length; i++) {
                pair[i] = pair[i].replaceFirst("^\\s*(.*?)\\s*$", "$1");
            }
        }
        return pair;
    }

    protected void setConfig(String data) {
        if (data != null) {
            setSource();
            String values[] = data.split(lineSep);
            for (int i = 0; i < values.length; i++) {
                String item = strip(values[i]);
                String[] pair = parsePair(item);
                readWriteField(pair[0], pair[1]);
            }
        }
    }

    protected String getConfig() {
        setSource();
        ArrayList<String> data = new ArrayList<String>();
        HashMap<String, Field> fnht = getFieldNames();
        String name, value;
        Field field;
        Iterator<String> en = fnht.keySet().iterator();
        while (en.hasNext()) {
            field = fnht.get(en.next());
            name = field.getName();
            value = readWriteField(name, null);
            if (value != null) {
                data.add(name + " = " + value);
            }
        }

        Collections.sort(data, new StringComparatorNoCase());
        String[] array = (String[]) data.toArray(new String[]{});
        return join(array, lineSep) + lineSep;
    }

    protected void readConfig() {
        String data = readTextFile(initPath, lineSep);
        setConfig(data);
    }

    protected void getDefaultValues() {
        defaultValues = getConfig();
        // parent.p(defaultValues);
    }

    protected void setDefaultValues() {
        setConfig(defaultValues);
    }

    private Rectangle parseRectangle(String value) {
        ArrayList<Integer> vi = new ArrayList<Integer>();
        Rectangle rect = new Rectangle();
        try {
            Matcher m = Pattern.compile("(-|\\d)+").matcher(value);
            while (m.find()) {
                vi.add(Integer.parseInt(m.group()));
            }
            if (vi.size() == 4) {
                Iterator<Integer> it = vi.iterator();
                rect = new Rectangle(it.next(), it.next(), it.next(), it.next());
            }
        } catch (Exception e) {
            System.out.println(getClass().getName() + ": error: " + e);
        }
        return rect;
    }

    private double getDouble(String s) {
        return Double.parseDouble(s);
    }

    private String formatNum(double v) {
        return String.format("" + v);
    }

    private String formatNum(int v) {
        return String.format("" + v);
    }

    private String readWriteField(String name, String value) {
        //parent.p("name = " + name + ", value = " + value);
        try {
            Class pc = source.getClass();
            //System.out.println("looking for [" + pair[0] + "]");
            Field f = pc.getDeclaredField(name);
            Object obj = null;
            try {
                obj = f.get(source); // get the class instance
            } catch (Exception e) {
                // trap unreadable classes
            }
            if (obj != null) {
                String classType = f.getType().toString();
                //System.out.println("name: " + name + ", classtype: " + classType);
                classType = classType.replaceFirst(".*\\.(.*)", "$1");
                boolean write = (value != null);
                if (write) {
                    value = strip(value);
                }
                if (classType.equals("int")) {
                    if (write) {
                        f.setInt(source, Integer.parseInt(value));
                        //parent.p("name: " + name +", int: " + f.getInt(source));
                    } else {
                        value = formatNum(f.getInt(source));
                    }
                } else if (classType.equals("double")) {
                    if (write) {
                        f.setDouble(source, getDouble(value));
                    } else {
                        value = "" + formatNum(f.getDouble(source));
                    }
                } else if (classType.equals("MutableDouble")) {
                    if (write) {
                        ((MutableDouble) obj).v = getDouble(value);
                    } else {
                        value = formatNum(((MutableDouble) obj).v);
                    }
                } else if (classType.equals("boolean")) {
                    if (write) {
                        f.setBoolean(source, value.equals("true"));
                    } else {
                        value = "" + f.getBoolean(source);
                    }
                } else if (classType.equals("String")) {
                    if (write) {
                        f.set(source, value);
                    } else {
                        value = (String) f.get(source);
                    }
                } // This is so terrible. Surely there's an easier way
                else if (classType.equals("Rectangle")) {
                    if (write) {
                        Rectangle r = parseRectangle(value);
                        if (r != null) {
                            f.set(source, r);
                        }
                    } else {
                        value = (String) f.get(source).toString();
                    }
                } else if (classType.equals("JRadioButton")) {

                    JRadioButton button = (JRadioButton) obj;
                    if (write) {
                        button.setSelected(value.equals("true"));
                    } else {
                        value = "" + button.isSelected();
                    }
                } else if (classType.equals("JCheckBox")) {

                    JCheckBox cb = (JCheckBox) obj;
                    if (write) {
                        cb.setSelected(value.equals("true"));
                    } else {
                        value = "" + cb.isSelected();
                    }
                } else if (classType.equals("JTextField")) {
                    JTextField tf = (JTextField) obj;
                    if (write) {
                        tf.setText(value);
                    } else {
                        value = tf.getText();
                    }
                } else if (classType.equals("JComboBox")) {
                    JComboBox box = (JComboBox) obj;
                    if (write) {
                        box.setSelectedIndex(Integer.parseInt(value));
                    } else {
                        value = "" + box.getSelectedIndex();
                    }
                } else if (classType.equals("JSpinner")) {
                    JSpinner box = (JSpinner) obj;
                    if (write) {
                        box.setValue(Integer.parseInt(value));
                    } else {
                        value = "" + box.getValue();
                    }
                } else if (classType.equals("JScrollBar")) {
                    JScrollBar box = (JScrollBar) obj;
                    if (write) {
                        int v = Integer.parseInt(value);
                        box.setValue(v);
                    } else {
                        value = "" + box.getValue();
                    }
                } else if (classType.equals("Frame")) {
                    // only to save/restore screen geometry
                    Frame tp = (Frame) obj;
                    if (write) {
                        Rectangle r = parseRectangle(value);
                        if (r != null) {
                            tp.setBounds(r);
                        }
                    } else {
                        value = "" + tp.getBounds();
                    }
                } else if (classType.equals("JTabbedPane")) {
                    JTabbedPane tp = (JTabbedPane) obj;
                    if (write) {
                        tp.setSelectedIndex(Integer.parseInt(value));
                    } else {
                        value = "" + tp.getSelectedIndex();
                    }
                } else {
                    System.out.println(getClass().getName() + ": cannot decode value for " + classType + " (" + name + ")");
                }
            }
        } catch (Exception e) {
            System.out.println(getClass().getName() + ":readWriteField: \"" + name + "\": " + e);
        }
        return value;
    }

    protected void writeConfig() {
        String output = getConfig();
        //parent.p(String.format("%s",output));
        try {
            BufferedWriter bw = new BufferedWriter(new FileWriter(initPath));
            bw.write(output);
            bw.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    private HashMap<String, Field> getFieldNames() {
        HashMap<String, Field> hash = new HashMap<String, Field>();
        Field[] fields = source.getClass().getDeclaredFields();
        String name;
        for (Field field : fields) {
            name = field.getName();
            hash.put(name, field);
        }
        return hash;
    }

    private String readTextFile(String path, String lineSep) {
        String s = null;
        File f = new File(path);
        if (f.exists()) {
            StringBuilder sb = new StringBuilder();
            try {
                BufferedReader br = new BufferedReader(new FileReader(f));
                String line;
                while ((line = br.readLine()) != null) {
                    sb.append(line).append(lineSep);
                }
                br.close();
                s = sb.toString();
            } catch (Exception e) {
                //System.out.println(e);
            }
        }
        return s;
    }

    private boolean testMakeDirs(String path) {
        File fpath = new File(path);
        if (fpath.exists()) {
            return false;
        } else {
            fpath.mkdirs();
            return true;
        }
    }

    private String join(String[] array, String delim) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                sb.append(delim);
            }
            sb.append(array[i]);
        }
        return sb.toString();
    }

    private String strip(String s) {
        return s.replaceFirst("^\\s*(.*)\\s*$", "$1");
    }
}
