package org.twopm.laf;

import java.awt.Font;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.ColorUIResource;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import org.twopm.laf.tools.Scheme2Java;
import org.openide.util.Exceptions;
import org.openide.windows.WindowManager;

/**
 * @author Edward M. Kagan <pagan@tabor.one>
 */
@SuppressWarnings("rawtypes")
public class Kernel {
    
    private static final boolean w_init = false;
    public static Color[] color_scheme_loaded;
    private static final String scheme_file_name = "netbeans.twopm_scheme";
    
    public static void load_kernel ()
    {
        if (UIManager.getLookAndFeel().getName().equals("[twopm!] Titan")) //NOI18N
        {
            String nb_etc = retrieve_netbeans_etc_dir();
            String cfh_dir = retrieve_app_folder();
            boolean a = (new File (cfh_dir).mkdirs());
            load_setup (cfh_dir);
            TitanTheme.self_tuneup(color_scheme_loaded);
            reset_html_link_color();
            apply_border_managment_hints();
            reloadUI();
            if (w_init)
            {
               Scheme2Java.printJava(color_scheme_loaded);
            }
        }
        else
        {
            System.err.println("\"Titan\" Look and Feel is not selected"
                    + " - no modifications done. Follow your ugly way of live.");
        }
    }
    
    public static boolean setup_font_antialiaing_hints ()
    {
        try 
        {   
            String nb_etc = retrieve_netbeans_etc_dir ();
            System.setProperty("awt.useSystemAAFontSettings","lcd");
            System.setProperty("swing.aatext", "true");
         
            Path path = FileSystems.getDefault().getPath(nb_etc, "netbeans.conf");
            if( Files.isWritable(path))
            {

                List<String> linesL =  Files.readAllLines(path, StandardCharsets.UTF_8);
                String [] lines = new String[linesL.size()];

                int i = 0;
                for (String str : linesL)
                {
                    lines[i] = str;
                    i++;
                }

                for (i =0; i < lines.length; i ++)
                {
                    if (lines[i].contains("netbeans_default_options="))
                    {
                        if (!lines[i].contains("-J-Dawt.useSystemAAFontSettings"))
                        {
                            System.err.println("No rendering hints defined - fixing...");
                            String [] parts = lines[i].split("\"");
                            String new_line = parts[0] + "\"" + parts[1] + " -J-Dswing.aatext=true -J-Dawt.useSystemAAFontSettings=lcd" + "\"";
                            lines[i] = new_line;
                            write_scheme(nb_etc + "netbeans.conf", lines);
                            System.out.println("Restart Netbeabs to see the effect.");
                        }
                    }
                }
                return true;
            }
            else
            {
                System.err.println("Unable to fix config - bye-bye :(");
                return false;
            }
        } 
        catch (IOException ex) 
        {
            Exceptions.printStackTrace(ex);
            System.err.println("Unable to apply rendering hints :( Sorry...");
            return false;
        }
    }
    
    private static void apply_border_managment_hints ()
    {
        javax.swing.border.EmptyBorder invisable_border = new EmptyBorder(0,0,0,0);
        UIManager.put ("EditorPane.border", invisable_border); // Editor FUCKING LINE!
        UIManager.put ("ComboBox.border", invisable_border); // Editor FUCKING LINE!   
        
        javax.swing.border.EmptyBorder spinner_border = new EmptyBorder(1,1,1,1);
        
        UIManager.put("Spinner.border", spinner_border);
        UIManager.put("Spinner.arrowButtonBorder", spinner_border);

        UIManager.put ("TabbedContainer.editor.outerBorder", 
                new LineBorder(new java.awt.Color(24, 24, 24, 255), 1));        // Editor border
        
        javax.swing.border.LineBorder light_dark_bodrer = 
                new LineBorder(new java.awt.Color(0, 0, 0, 31), 2);
        UIManager.put ("TabbedContainer.view.outerBorder", light_dark_bodrer); // Navigator/Project/etc. round border!!
        
        // <editor-fold desc="Buttonized components managment" defaultstate="collapsed">
   
        javax.swing.border.CompoundBorder buttons_border = 
                new CompoundBorder(light_dark_bodrer, new EmptyBorder(4,6,4,6));
        
        String [] buttonized_components = {
            "ToggleButton.border",
            "Button.border",
            "ToggleButton.border",
        } ;
        
        javax.swing.border.LineBorder text_bodrer = 
                new LineBorder(new java.awt.Color(0, 0, 0, 31), 1);
        
        String [] text_components = {
            "CheckBox.border",
            "FormattedTextField.border",
            "PasswordField.border",
            "TextField.border",
            "RadioButton.border",
            "TextArea.border",
            "TextPane.border",
            "SplitPane.border"
        } ;
        
        for (String o : text_components)
        {
            UIManager.put(o, text_bodrer);
        }
        
        for (String o : buttonized_components)
        {
            UIManager.put(o, buttons_border);
        }
        // </editor-fold>
        
        // <editor-fold desc="Menu components managment" defaultstate="collapsed">
     
        javax.swing.border.EmptyBorder mainMenuBorder = new EmptyBorder(2,4,2,4);
        javax.swing.border.EmptyBorder itemBorder = new EmptyBorder(4,4,4,4);
        UIManager.put ("MenuBar.border", buttons_border);
        UIManager.put ("Menu.border", mainMenuBorder);
        UIManager.put ("MenuItem.border", itemBorder);
        
        
         
       
          String [] side_components ={    
            "CheckBoxMenuItem.border",
            "ScrollPane.border",
            "RadioButtonMenuItem.border",
            "PopupMenu.border"
        };
        
        javax.swing.border.EmptyBorder empty_1p_border = new EmptyBorder(1,1,1,1);
          
        for (String o : side_components)
        {
            UIManager.put(o, empty_1p_border);
        }
        
         // </editor-fold>
        
        // <editor-fold desc="Status components" defaultstate="collapsed">
   
        javax.swing.border.EmptyBorder ministatusbarBorder = new EmptyBorder(2,4,2,4);
        UIManager.put ("nb.explorer.ministatusbar.border", ministatusbarBorder);
        
        javax.swing.border.EmptyBorder status_border = new EmptyBorder(4,2,4,2);

        String [] status = 
        {
            "Nb.Editor.Toolbar.border",
            "Nb.Editor.Status.leftBorder",
            "Nb.Editor.Status.rightBorder",
            "Nb.Editor.Status.onlyOneBorder",
            "Nb.Explorer.Status.border",
            "Nb.Editor.Status.innerBorder",
            "Table.scrollPaneBorder",
            "TableHeader.cellBorder",
            "OptionPane.border",
            //"Tree.editorBorder",
            "Table.focusCellHighlightBorder",
            "ToolTip.border",
            "List.focusCellHighlightBorder",
            "nb.quicksearch.border"
        };
        
        for (String o : status)
        {
            UIManager.put(o, status_border);
        }
        // </editor-fold>
    }
    
    
    // <editor-fold desc="Routing and filesystem routines" defaultstate="collapsed">
    
    private static String retrieve_app_folder ()
    {
        if (System.getProperty("os.name").startsWith("Windows")) {
            return System.getenv("APPDATA") + File.separator + "twopm_scheme";
        } else {
            return retrieve_netbeans_etc_dir ();
        }    
    }
    
    private static String retrieve_netbeans_etc_dir ()
    {
        String nb_home = System.getProperty("netbeans.home");
        String nb_conf = nb_home.substring(0, nb_home.length() - "platform".length()) + "etc" + File.separator;
        return nb_conf;
    }
    // </editor-fold>
   
    
    // <editor-fold desc="Load/Save routines."  defaultstate="collapsed">
    
    private static boolean load_setup (String nb_etc)
    {
        File scheme_file = new File(nb_etc + File.separator + scheme_file_name);
        String path = scheme_file.getAbsolutePath();
        
        if (!scheme_file.exists())
        {
            if (w_init)
            {
                init_scheme();
                if (save_scheme(path))
                {
                    if (read_scheme (path))
                    {
                        reloadUI();
                        return true;
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                color_scheme_loaded = Defaults.preload_defaults();
                reloadUI();
                return true;
            }
        }
        else
        {
            if (read_scheme (path))
            {
                reloadUI();
                return true;
            }
        }
        return false;
    }

    private static Random rand = new Random(System.currentTimeMillis());
    
    public static void save ()
    {
        String nb_etc = retrieve_app_folder();
        File scheme_file = new File(nb_etc + File.separator + scheme_file_name);
        String path = scheme_file.getAbsolutePath();
        save_scheme (path);
    }
    
    public static void reload ()
    {
        String nb_etc = retrieve_app_folder();
        File scheme_file = new File(nb_etc + File.separator + scheme_file_name);
        String path = scheme_file.getAbsolutePath();
        read_scheme (path);
        reloadUI();
    }
    
    public static void load (String path)
    {
        read_scheme (path);
        reloadUI();
    }
    
    private static void init_scheme ()
    {
        ArrayList<Color> color_map = new ArrayList<Color>();
        for (int i = 0; i < Keys.color_keys.length; i ++)
        {
            Object o = UIManager.get(Keys.color_keys[i]);
            if (o != null)
            {
                if (o.getClass().equals(javax.swing.plaf.ColorUIResource.class))
                {
                    ColorUIResource oo = (ColorUIResource) o;
                    Color c = new Color(Keys.color_keys[i], Color.ColorClass.SF, oo.getRed(), oo.getGreen(), oo.getBlue(), oo.getAlpha(),"...");
                    color_map.add(c);
                }
                else if (o.getClass().equals(java.awt.Color.class))
                {
                    java.awt.Color oo = (java.awt.Color) o;
                    Color c = new Color(Keys.color_keys[i], Color.ColorClass.AT, oo.getRed(), oo.getGreen(), oo.getBlue(), oo.getAlpha(),"...");
                    color_map.add(c);
                }
            }
            else
            {
                ColorUIResource oo = new ColorUIResource(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
                Color c = new Color(Keys.color_keys[i], Color.ColorClass.SF, oo.getRed(), oo.getGreen(), oo.getBlue(), oo.getAlpha(),"...");
                color_map.add(c);
            }
        }
        
        color_scheme_loaded = new Color[color_map.size()];
        color_map.toArray(color_scheme_loaded);
    }
    
    
    private static boolean save_scheme(String scheme_file) {
        
        Path path = FileSystems.getDefault().getPath(scheme_file);
            
        try 
            {
                PrintWriter writer = new PrintWriter(scheme_file , "UTF-8");
                for (int i = 0; i < color_scheme_loaded.length; i ++)
                {
                    writer.println(color_scheme_loaded[i].toStringC());
                }
                writer.close();
                return true;
            } 
            catch (FileNotFoundException ex) 
            {
                Exceptions.printStackTrace(ex);
                return false;
            } 
            catch (UnsupportedEncodingException ex) 
            {
                Exceptions.printStackTrace(ex);
                return false;
            }
        //}
//        else
//        {
//            JOptionPane.showMessageDialog(null, "Unable to write '" + scheme_file + "'" );
//            return false;
//        }
    }

    private static boolean read_scheme(String scheme_file) {
        
        try 
        {
            ArrayList<Color> color_map = new ArrayList<Color>();
            List<String> C = Files.readAllLines(Paths.get(scheme_file), Charset.forName("UTF-8"));
        
            for (String s : C)
            {
                Color toLoad = new Color(s);
                for (String key : Keys.color_keys)
                {
                    if (toLoad.getName().equals(key))
                    {
                        color_map.add(toLoad);
                        break;
                    }
                }
            }

            color_scheme_loaded = new Color[color_map.size()];
            color_map.toArray(color_scheme_loaded);
            
            return true;
        } 
        catch (IOException ex) 
        {
            Exceptions.printStackTrace(ex);
            return false;
        }
    }

    public static void reloadUI()
    {
        for (int i = 0; i < color_scheme_loaded.length; i ++)
        {
            UIManager.put(color_scheme_loaded[i].getName(), color_scheme_loaded[i].getObject());
        }
        repaintUI();
    }
    
    private static void repaintUI()
    {
        SwingUtilities.updateComponentTreeUI(WindowManager.getDefault().getMainWindow());
        WindowManager.getDefault().getMainWindow().pack();
        WindowManager.getDefault().getMainWindow().repaint();
    }
    
    private static void write_scheme(String path, String [] data) throws IOException 
    {
        File fout = new File(path);
        FileOutputStream fos = new FileOutputStream(fout);

        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));

        for (int i = 0; i < data.length; i ++) 
        {
            bw.write(data[i]);
            bw.newLine();
        }

        bw.close();
    }

    // </editor-fold>
    
    public static Color[] getColors() 
    {
        if (color_scheme_loaded == null)
        {
            return new Color[0];
        }
        else
        {
            return color_scheme_loaded;
        }
    }


    private static void reset_html_link_color()
    {
        for (int i = 0; i < color_scheme_loaded.length; i ++)
        {
            if (color_scheme_loaded[i].getName().equals("nb.laf.postinstall.callable.httplink"))// NOI18N
            {
                final int  index = i;
                UIManager.put( "nb.laf.postinstall.callable", new Callable<Object>() { //NOI18N

                    @Override
                    public Object call() throws Exception {
                        HTMLEditorKit kit = new HTMLEditorKit();
                        StyleSheet newStyleSheet = new StyleSheet();
                        Font f = new JLabel().getFont();
                        newStyleSheet.addRule(new StringBuffer("body { font-size: ").append(f.getSize()) // NOI18N
                                    .append("; font-family: ").append(f.getName()).append("; }").toString()); // NOI18N
                        newStyleSheet.addRule( "a { color: " +color_scheme_loaded[index].getHEX() + "; text-decoration: underline}"); //NOI18N
                        newStyleSheet.addStyleSheet(kit.getStyleSheet());
                        kit.setStyleSheet(newStyleSheet);
                        return null;
                    }
                });
                break;
            }
        }
    }
    
}
