/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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 org.apache.cayenne.modeler.dialog.pref;

import java.awt.Component;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Vector;

import javax.swing.DefaultComboBoxModel;

import org.apache.cayenne.modeler.util.CayenneController;
import org.apache.cayenne.swing.BindingBuilder;
import org.apache.cayenne.swing.ObjectBinding;
import org.apache.cayenne.util.Util;

/**
 * A controller for stream encoding picker component.
 * 
 * @author Andrei Adamchik
 */
public class EncodingSelector extends CayenneController {

    public static final String ENCODING_PROPERTY_BINDING = "encoding";

    protected PropertyChangeListener encodingChangeListener;
    protected ObjectBinding defaultEncodingBinding;
    protected ObjectBinding otherEncodingBinding;
    protected ObjectBinding selectedEncodingBinding;

    protected EncodingSelectorView view;
    protected String systemEncoding;
    protected String encoding;
    protected boolean defaultEncoding;

    /**
     * Creates new EncodingPicker.
     */
    public EncodingSelector(CayenneController parent) {
        this(parent, new EncodingSelectorView());
    }

    public EncodingSelector(CayenneController parent, EncodingSelectorView view) {
        super(parent);
        this.view = view;

        initBindings();
    }

    public Component getView() {
        return view;
    }

    protected void initBindings() {
        // init static models...
        this.systemEncoding = detectPlatformEncoding();

        Vector allEncodings = supportedEncodings(systemEncoding);
        view.getEncodingChoices().setModel(new DefaultComboBoxModel(allEncodings));
        view.getDefaultEncodingLabel().setText("Default (" + systemEncoding + ")");
        view.getDefaultEncoding().setSelected(true);

        // create bindings...
        BindingBuilder builder = new BindingBuilder(
                getApplication().getBindingFactory(),
                this);

        this.defaultEncodingBinding = builder
                .bindToStateChange(view.getDefaultEncoding(), "defaultEncoding");

        this.otherEncodingBinding = builder.bindToStateChange(view.getOtherEncoding(),
                "otherEncoding");

        this.selectedEncodingBinding = builder.bindToComboSelection(view
                .getEncodingChoices(), "encoding");
    }

    /**
     * Returns default encoding on the current platform.
     */
    protected String detectPlatformEncoding() {
        // this info is private until 1.5, so have to hack it...
        return new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding();
    }

    /**
     * Returns an array of charsets that all JVMs must support cross-platform combined
     * with a default platform charset. See Javadocs for java.nio.charset.Charset for the
     * list of "standard" charsets.
     */
    protected Vector supportedEncodings(String platformEncoding) {
        String[] defaultCharsets = new String[] {
                "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16"
        };

        Vector charsets = new Vector(Arrays.asList(defaultCharsets));
        if (!charsets.contains(platformEncoding)) {
            charsets.add(platformEncoding);
        }

        Collections.sort(charsets);
        return charsets;
    }

    public void bindingUpdated(String expression, Object newValue) {
        if (ENCODING_PROPERTY_BINDING.equals(expression)) {
            this.encoding = (newValue != null) ? newValue.toString() : null;
            this.defaultEncoding = encoding == null || encoding.equals(systemEncoding);

            selectedEncodingBinding.updateView();
            if (defaultEncoding) {
                defaultEncodingBinding.updateView();
                view.getEncodingChoices().setEnabled(false);
                view.getDefaultEncodingLabel().setEnabled(true);
            }
            else {
                otherEncodingBinding.updateView();
                view.getEncodingChoices().setEnabled(true);
                view.getDefaultEncodingLabel().setEnabled(false);
            }
        }
    }

    // ===============
    //    Properties
    // ===============

    public void setEncoding(String encoding) {
        if (!Util.nullSafeEquals(this.encoding, encoding)) {
            Object old = this.encoding;

            this.encoding = encoding;
            firePropertyChange("encoding", old, encoding);
        }
    }

    public String getEncoding() {
        return encoding;
    }

    public boolean isDefaultEncoding() {
        return defaultEncoding;
    }

    public void setDefaultEncoding(boolean b) {
        if (b != defaultEncoding) {
            this.defaultEncoding = b;

            if (b) {
                setEncoding(systemEncoding);
                view.getEncodingChoices().setEnabled(false);
                view.getDefaultEncodingLabel().setEnabled(true);
            }
            else {
                view.getEncodingChoices().setEnabled(true);
                view.getDefaultEncodingLabel().setEnabled(false);
            }

        }
    }

    public boolean isOtherEncoding() {
        return !isDefaultEncoding();
    }

    public void setOtherEncoding(boolean b) {
        setDefaultEncoding(!b);
    }
}
