// This file is part of GNOME Boxes. License: LGPLv2+
// Boxes.EditableEntry is based on gnome-control-center CcEditableEntry

using Gtk;

private const string EMPTY_TEXT = "\xe2\x80\x94";

private class Boxes.EditableEntry: Alignment {
    private enum Page {
        LABEL,
        BUTTON,
        ENTRY
    }

    public signal void editing_done ();

    private string _text;
    public string text {
        get { return _text; }
        set {
            _text = value;
            entry.text = value;

            if (value == null || value.length == 0)
                value = EMPTY_TEXT;

            label.label = value;
            (button.get_child () as Label).label = value;
        }
    }

    public bool _editable;
    public bool editable {
        get { return _editable; }
        set {
            if (value == _editable)
                return;

            _editable = value;
            notebook.page = editable ? Page.BUTTON : Page.LABEL;
        }
    }

    public bool selectable {
        get { return label.selectable; }
        set {
            if (value == label.selectable)
                return;

            label.selectable = value;
        }
    }

    private Pango.Weight _weight = Pango.Weight.NORMAL;
    public Pango.Weight weight {
        get { return _weight; }
        set {
            if (value == _weight && weight_set)
                return;

            _weight = value;
            weight_set = true;

            update_fonts ();
        }
    }
    public bool weight_set { get; set; }

    private double _scale = 1.0;
    public double scale {
        get { return _scale; }
        set {
            if (value == _scale && scale_set)
                return;

            _scale = value;
            scale_set = true;

            update_fonts ();
        }
    }
    public bool scale_set { get; set; }

    private Gtk.Notebook notebook;
    private Gtk.Label label;
    private Gtk.Button button;
    private Gtk.Entry entry;

    private void update_entry_font (Gtk.Entry entry) {
        if (!scale_set && !weight_set)
            return;

        SignalHandler.block_by_func (entry, (void*)update_entry_font, this);

        entry.override_font (null);
        var desc = entry.get_style_context ().get_font (entry.get_state_flags ());
        if (weight_set)
            desc.set_weight (weight);
        if (scale_set)
            desc.set_size ((int)(scale * desc.get_size ()));
        entry.override_font (desc);

        SignalHandler.unblock_by_func (entry, (void*)update_entry_font, this);
    }

    private void update_fonts () {
        var attrs = new Pango.AttrList ();

        if (scale_set)
            attrs.insert (Pango.attr_scale_new (scale));
        if (weight_set)
            attrs.insert (Pango.attr_weight_new (weight));

        label.set_attributes (attrs);
        (button.get_child () as Label).set_attributes (attrs);
        update_entry_font (entry);
    }

    private void start_editing () {
        notebook.page = Page.ENTRY;
    }

    private bool in_stop_editing;
    private void stop_editing () {
        /* Avoid launching another "editing-done" signal
         * caused by the notebook page change */
        if (in_stop_editing)
            return;

        in_stop_editing = true;
        notebook.page = Page.BUTTON;
        text = entry.text;
        editing_done ();
        in_stop_editing = false;
    }

    private void cancel_editing () {
        entry.text = text;
        notebook.page = Page.BUTTON;
    }

    public EditableEntry () {
        notebook = new Gtk.Notebook ();
        notebook.show_tabs = false;
        notebook.show_border = false;

        label = new Gtk.Label (EMPTY_TEXT);
        label.set_alignment (0.0f, 0.5f);
        notebook.append_page (label, null);

        button = new Gtk.Button.with_label (EMPTY_TEXT);
        button.receives_default = true;
        button.relief = Gtk.ReliefStyle.NONE;
        button.set_alignment (0.0f, 0.5f);
        notebook.append_page (button, null);
        button.clicked.connect (() => {
            start_editing ();
        });

        (button.get_child ()).size_allocate.connect ((widget, allocation) => {
                Gtk.Allocation alloc;

                widget.get_parent ().get_allocation (out alloc);
                var offset = allocation.x - alloc.x;
                if (offset != label.xpad)
                    label.set_padding (offset, 0);

        });

        entry = new Gtk.Entry ();
        notebook.append_page (entry, null);
        entry.activate.connect (() => {
            stop_editing ();
        });

        entry.focus_out_event.connect (() => {
            stop_editing ();
            return false;
        });

        entry.key_press_event.connect ((widget, event) => {
            if (event.keyval == Gdk.Key.Escape)
                cancel_editing ();

            return false;
        });

        entry.style_updated.connect ((entry) => {
            update_entry_font (entry as Gtk.Entry);
        });

        notebook.page = Page.LABEL;
        this.add (notebook);
        this.show_all ();
    }
}
