/*
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License (the License). You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
 * or http://www.netbeans.org/cddl.txt.
 * 
 * When distributing Covered Code, include this CDDL Header Notice in each file
 * and include the License file at http://www.netbeans.org/cddl.txt.
 * If applicable, add the following below the CDDL Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.core.output;

import java.awt.Color;
import java.awt.Font;
import java.util.ResourceBundle;
import java.io.File;
import javax.swing.JTextPane;
import javax.swing.UIManager;

import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.options.SystemOption;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;

/** Settings for output window.
*
* @author Petr Hamernik
*/
public class OutputSettings extends SystemOption {
    /** generated Serialized Version UID */
    static final long serialVersionUID = 5773055866277884154L;

    /** bundle to obtain text information from */
    private static ResourceBundle bundle;

    /** Property name of the fontSize property */
    public static final String PROP_FONT_SIZE = "fontSize"; // NOI18N
    /** Property name of the tabSize property */
    public static final String PROP_TAB_SIZE = "tabSize"; // NOI18N
    /** Property name of the foreground property */
    public static final String PROP_FOREGROUND = "foreground"; // NOI18N
    /** Property name of the cursorForeground property */
    public static final String PROP_CURSOR_FOREGROUND = "cursorForeground"; // NOI18N
    /** Property name of the jumpCursorForeground property */
    public static final String PROP_JUMP_CURSOR_FOREGROUND = "jumpCursorForeground"; // NOI18N
    /** Property name of the jumpCursorForeground property */
    public static final String PROP_JUMP_LINK_FOREGROUND = "jumpLinkForeground"; // NOI18N    
    /** Property name of the background property */
    public static final String PROP_BACKGROUND = "background"; // NOI18N
    /** Property name of the cursorBackground property */
    public static final String PROP_CURSOR_BACKGROUND = "cursorBackground"; // NOI18N
    /** Property name of the jumpCursorBackground property */
    public static final String PROP_JUMP_CURSOR_BACKGROUND = "jumpCursorBackground"; // NOI18N
    /** Property name of the historySize property */
    public static final String PROP_HISTORY_SIZE = "historySize"; // NOI18N
    /** Property name of the redirection property */
    public static final String PROP_REDIRECTION = "redirection"; // NOI18N
    /** Property name of the redirection location property */
    public static final String PROP_DIRECTORY = "directory"; // NOI18N
    /** Property name of the selectionBackground property */
    public static final String PROP_SELECTION_BACKGROUND = "selectionBackground"; // NOI18N
    
    private static final String REDIR_FOLDER = "output";           // NOI18N
    
    private static int fontSize;
    static {
        //issue 33875, font size should be based on -fontsize/I10N supplied size
        java.awt.Font f = 
            javax.swing.UIManager.getFont ("controlFont"); //NOI18N
        if (f != null) {
            fontSize = f.getSize();
        } else {
            fontSize = 12;
        }
    }
    
    private static int tabSize = 8;
    private static int historySize = 10000;
    private static boolean redirection = false;
    
    private static File directory;
    
    /** Holds value of property selectionBackground. */
    private static Color selectionBackground;
    
    static {
        String userDir = System.getProperty ("netbeans.user");

        if (userDir == null)
            userDir = System.getProperty ("netbeans.home");
        if (userDir != null) {
            userDir = new File(userDir).getAbsolutePath();

            directory = new File (userDir, REDIR_FOLDER);
            directory.mkdirs();
        } else {
            // Running in e.g. org.netbeans.core.Plain.
            // Just pick some random location.
            directory = new File(System.getProperty("user.dir")); // NOI18N
        }
    }
    
    public OutputSettings () {
    }

    public String displayName () {
        return getString("CTL_Output_settings");
    }

    public HelpCtx getHelpCtx () {
        return new HelpCtx (OutputSettings.class);
    }

    private int sz=-1;
    public int getFontSize() {
        if (sz == -1) {
            Font std = UIManager.getFont ("controlFont"); //NOI18N
            if (std != null) {
                sz = std.getSize();
            } else {
                sz = new JTextPane().getFont().getSize();
            }
        }
        return sz;
    }

    public boolean isRedirection() {
        return redirection;
    }

    public void setRedirection(boolean redirection) {
        if (this.redirection != redirection) {
            Boolean old = this.redirection ? Boolean.TRUE : Boolean.FALSE;
            this.redirection = redirection;
            firePropertyChange (PROP_REDIRECTION, old, redirection ? Boolean.TRUE : Boolean.FALSE);
        }
    }
    
    public File getDirectory() {
        return directory;
    }

    public void setDirectory(File s) {
        if (directory != s) {
            //issue 32911, should not be possible to set redirection to a nonexistent dir
            File old = directory;
            directory = s;
            firePropertyChange (PROP_DIRECTORY, old, directory);
        }
    }
    
    boolean checkRedirectionDirExists(File s) {
        if (!s.exists()) {
            DialogDescriptor dd = new DialogDescriptor (
                NbBundle.getMessage (OutputSettings.class, 
                    "FMT_NO_DIR", s), //NOI18N
                NbBundle.getMessage (OutputSettings.class, "TTL_NO_DIR"),
                true, DialogDescriptor.YES_NO_OPTION, 
                DialogDescriptor.YES_OPTION, 
                DialogDescriptor.DEFAULT_ALIGN, 
                HelpCtx.DEFAULT_HELP, null);

            Object retVal = DialogDisplayer.getDefault().notify(dd);

            if (retVal == dd.YES_OPTION) {
                try {
                    if (!s.mkdir()) {
                    DialogDescriptor dd1 = new DialogDescriptor (
                        NbBundle.getMessage (OutputSettings.class, 
                            "MSG_CREATE_FAIL"), //NOI18N
                        NbBundle.getMessage (OutputSettings.class, 
                            "TTL_NO_DIR"), //NOI18N
                        true, DialogDescriptor.WARNING_MESSAGE, 
                        DialogDescriptor.YES_OPTION, 
                        DialogDescriptor.DEFAULT_ALIGN, 
                        HelpCtx.DEFAULT_HELP, null);

                        DialogDisplayer.getDefault().notify (dd1);
                        return false;
                    }
                } catch (Exception e) {
                    ErrorManager.getDefault().annotate (e, 
                        NbBundle.getMessage (OutputSettings.class, 
                        "MSG_CREATE_FAIL")); //NOI18N
                    ErrorManager.getDefault().notify(ErrorManager.USER, 
                        e);
                    return false;
                }
            } else {
                return false;
            }
        }
        return true;
    }
    
    /** Tab size getter */
    public int getTabSize() {
        return tabSize;
    }

    /** Tab size setter */
    public void setTabSize(int tabSize) {
        if (this.tabSize != tabSize) {
            this.tabSize = tabSize;
            change();
        }
    }
    
    /** History size getter */
    public int getHistorySize() {
        return historySize;
    }

    /** History size setter */
    public void setHistorySize(int historySize) {
        if (this.historySize != historySize) {
            this.historySize = historySize;
            change();
        }
    }

    public Color getBaseForeground() {
        Color result = UIManager.getColor("nb.output.foreground");
        if (result == null) {
            result = UIManager.getColor("textText"); //NOI18N
        }
        return result == null ? Color.black : result;
    }

    public Color getCursorForeground() {
        Color result = UIManager.getColor("text"); //NOI18N
        return result == null ? Color.white : result;
    }

    public Color getJumpCursorForeground() {
        Color result = UIManager.getColor("text"); //NOI18N
        return result == null ? Color.white : result;
    }
    
    private static Color jumpLinkForeground = null;
    public Color getJumpLinkForeground() {
        if (jumpLinkForeground == null) {
            Color result = UIManager.getColor("nb.hyperlink.foreground"); //NOI18N

            if (result == null) {
                Color bg = getBaseBackground();
                result = Color.blue;
                float[] f = new float[3];
                Color.RGBtoHSB(bg.getRed(), bg.getGreen(), bg.getBlue(), f);
                if (f[2] < 0.6) {
                    Color.RGBtoHSB (0, 0, 255, f);
                    f[1] = Math.min (0f, f[1] - 0.3f);
                    result = new Color(Color.HSBtoRGB(f[0], f[1], f[2]));
                }
            }
            jumpLinkForeground = result;
        }
        return jumpLinkForeground;
    }
    
    public Color getBaseBackground() {
        Color result = UIManager.getColor("nb.output.background");
        if (result == null) {
            result = UIManager.getColor("control"); // NOI18N 
        }
        return result == null ? Color.gray : result;
    }
    
    public Color getCursorBackground() {
        return getJumpLinkForeground();
    }

    public Color getJumpCursorBackground() {
        Color result = UIManager.getColor("controlHighlight"); //NOI18N
        return result == null ? getBaseBackground().brighter() : result;
    }
    
    /** Getter for property selectionBackground.
     * @return Value of property selectionBackground.
     */
    public Color getSelectionBackground() {
        Color result = UIManager.getColor("nb.output.selectionBackground"); //NOI18N
        if (result == null) {
            result = UIManager.getColor("List.selectionBackground"); // NOI18N
        }
        if (result == null) {
            result = new Color(204, 204, 255);
        } 
        //Hotfix for background color problems on win classic - the
        //list selection color is dark blue, and black is unreadable against
        //it.  Term does not appear to have a concept of selection *foreground*,
        //so not sure what better solution there is here.
        if (brightnessDiff (getBaseForeground(), result) < 96) {
            if (isBrighterThan (result, getBaseForeground())) {
                result = result.darker().darker();
            } else {
                result = findContrastingColor(result, getBaseForeground());
            }
        }
        return result;
    }
    
    private Color findContrastingColor (Color toReplace, Color contrast) {
        //FIXME - this is far from an intelligent algorithm.  Better
        //would be some heuristic for color perception, since it is possible
        //to have mathematically dissimilar colors that are nonetheless
        //unreadable when combined.  Still, this does produce a readable
        //result on all look and feels it was tried on.
        
        int[] toReplaceRGB = new int[] {  
            toReplace.getRed(),
            toReplace.getGreen(),
            toReplace.getBlue()
        };
        
        int[] toContrastRGB = new int[] {
            contrast.getRed(),
            contrast.getGreen(),
            contrast.getBlue()
        };
        
        int[] diffRGB = new int[] {
            toReplaceRGB[0] - toContrastRGB[0],
            toReplaceRGB[1] - toContrastRGB[1],
            toReplaceRGB[2] - toContrastRGB[2]
        };
        
        int[] resultRGB = new int[3];
        
        for (int i=0; i < 3; i++) {
            if (diffRGB[i] == 0) {
                diffRGB[i] = 16;
            }
            resultRGB[i] = toReplaceRGB[i];
            resultRGB[i] = Math.max (0, Math.min (255, resultRGB[i] + (4 * diffRGB[i])));
        }
        return new Color (resultRGB[0],resultRGB[1],resultRGB[2]);
    }
    
    private int brightnessDiff (Color a, Color b) {
        int red = Math.abs(a.getRed() - b.getRed());
        int green = Math.abs(a.getGreen() - b.getGreen());
        int blue = Math.abs(a.getBlue() - b.getBlue());
        int avg = (red + green + blue) / 3;
        return avg;
    }
    
    private boolean isBrighterThan (Color a, Color b) {
        int red = a.getRed() - b.getRed();
        int green = a.getGreen() - b.getGreen();
        int blue = a.getBlue() - b.getBlue();
        return (red + green + blue) / 3 < 0;
    }

    //Setters for now hidden properties - retained for serialization
    //backward compatibility (this class should be rewritten to use 
    //registry anyway once that's public - TDB
    /** @deprecated - does nothing */
    public void setBaseForeground(Color c) {}
    /** @deprecated - does nothing */
    public void setCursorForeground(Color c) {}
    /** @deprecated - does nothing */
    public void setJumpCursorForeground(Color c) {}
    /** @deprecated - does nothing */
    public void setJumpLinkForeground(Color c) {}
    /** @deprecated - does nothing */
    public void setBaseBackground(Color c) { }
    /** @deprecated - does nothing */
    public void setCursorBackground(Color c) {}
    /** @deprecated - does nothing */
    public void setJumpCursorBackground(Color c) {}
    /** @deprecated - does nothing */
    public void setSelectionBackground(Color selectionBackground) {}
    /** @deprecated - does nothing */
    public void setFontSize(int fontSize) { }
    
    
    private void change() {
        firePropertyChange (null, null, null);
    }

    /** @return localized string */
    static String getString(String s) {
        if (bundle == null) {
            bundle = NbBundle.getBundle(OutputSettings.class);
        }
        return bundle.getString(s);
    }
    
}
