/**
 * Odoo, Open Source Management Solution
 * Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http:www.gnu.org/licenses/>
 *
 * Created on 7/1/15 5:10 PM
 */
package odoo.controls;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.odoo.core.orm.ODataRow;
import com.odoo.core.orm.OModel;
import com.odoo.core.orm.fields.OColumn;
import com.odoo.core.orm.fields.types.OBlob;
import com.odoo.core.orm.fields.types.OBoolean;
import com.odoo.core.orm.fields.types.ODate;
import com.odoo.core.orm.fields.types.ODateTime;
import com.odoo.core.orm.fields.types.OFloat;
import com.odoo.core.orm.fields.types.OHtml;
import com.odoo.core.orm.fields.types.OInteger;
import com.odoo.core.orm.fields.types.OSelection;
import com.odoo.core.orm.fields.types.OText;
import com.odoo.core.orm.fields.types.OTimestamp;
import com.odoo.core.orm.fields.types.OVarchar;
import com.odoo.R;

public class OField extends LinearLayout implements IOControlData.ValueUpdateListener {
    public static final String TAG = OField.class.getSimpleName();
    private Context mContext = null;
    private FieldType mType = FieldType.Text;
    private OColumn mColumn = null;
    private OModel mModel = null;
    private String mLabel, mField_name;
    private Object mValue = null;
    private boolean mEditable = false, showIcon = true, show_label = true;
    private TextView label_view = null;
    private int resId, tint_color = Color.BLACK, mValueArrayId = -1;
    private ImageView img_icon = null;
    private ViewGroup container = null;
    private Boolean with_bottom_padding = true, with_top_padding = true;
    private WidgetType mWidgetType = null;
    private String mParsePattern = null;
    private IOnChangeCallback mOnChangeCallback = null;
    private IOnDomainFilterCallbacks mOnDomainFilterCallbacks = null;
    private OColumn.ColumnDomain mColumnDomain = null;
    private float mWidgetImageSize = -1;
    private Boolean withPadding = true;
    // Controls
    private IOControlData mControlData = null;
    private Boolean useTemplate = true;
    private Integer defaultImage = -1;
    // Appearance
    private int textColor = Color.BLACK;
    private int labelColor = Color.DKGRAY;
    private int textAppearance = -1;
    private int labelAppearance = -1;
    private float textSize = -1;
    private float labelSize = -1;
    private IOnFieldValueChangeListener mValueUpdateListener = null;

    public enum WidgetType {
        Switch, RadioGroup, SelectionDialog, Searchable, SearchableLive, Image, ImageCircle, Duration;

        public static WidgetType getWidgetType(int widget) {
            switch (widget) {
                case 0:
                    return WidgetType.Switch;
                case 1:
                    return WidgetType.RadioGroup;
                case 2:
                    return WidgetType.SelectionDialog;
                case 3:
                    return WidgetType.Searchable;
                case 4:
                    return WidgetType.SearchableLive;
                case 5:
                    return WidgetType.Image;
                case 6:
                    return WidgetType.ImageCircle;
                case 7:
                    return WidgetType.Duration;
            }
            return null;
        }
    }

    public enum FieldType {
        Text, Boolean, ManyToOne, Chips, Selection, Date, Time, DateTime, Blob, RelationType;

        public static FieldType getTypeValue(int type_val) {
            switch (type_val) {
                case 0:
                    return FieldType.Text;
                case 1:
                    return FieldType.Boolean;
                case 2:
                    return FieldType.ManyToOne;
                case 3:
                    return FieldType.Chips;
                case 4:
                    return FieldType.Selection;
                case 5:
                    return FieldType.Date;
                case 6:
                    return FieldType.DateTime;
                case 7:
                    return FieldType.Blob;
                case 8:
                    return FieldType.Time;
            }
            return FieldType.Text;
        }
    }

    public OField(Context context) {
        super(context);
        init(context, null, 0, 0);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public OField(Context context, AttributeSet attrs, int defStyleAttr,
                  int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }

    public OField(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr, 0);
    }

    public OField(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0, 0);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr,
                      int defStyleRes) {
        mContext = context;
        if (attrs != null) {
            TypedArray types = mContext.obtainStyledAttributes(attrs,
                    R.styleable.OField);
            mField_name = types.getString(R.styleable.OField_fieldName);
            resId = types.getResourceId(R.styleable.OField_iconResource, 0);
            showIcon = types.getBoolean(R.styleable.OField_showIcon, true);
            tint_color = types.getColor(R.styleable.OField_iconTint, 0);
            show_label = types.getBoolean(R.styleable.OField_showLabel, true);
            int type_value = types.getInt(R.styleable.OField_fieldType, 0);
            mType = FieldType.getTypeValue(type_value);

            with_bottom_padding = types.getBoolean(
                    R.styleable.OField_withBottomPadding, true);
            with_top_padding = types.getBoolean(
                    R.styleable.OField_withTopPadding, true);
            mLabel = types.getString(R.styleable.OField_controlLabel);
            mValue = types.getString(R.styleable.OField_defaultValue);
            mParsePattern = types.getString(R.styleable.OField_parsePattern);
            mValueArrayId = types.getResourceId(
                    R.styleable.OField_valueArray, -1);
            mWidgetType = WidgetType.getWidgetType(types.getInt(
                    R.styleable.OField_widgetType, -1));
            mWidgetImageSize = types.getDimension(R.styleable.OField_widgetImageSize, -1);
            withPadding = types.getBoolean(R.styleable.OField_withOutSidePadding, true);

            textColor = types.getColor(R.styleable.OField_fieldTextColor, Color.BLACK);
            labelColor = types.getColor(R.styleable.OField_fieldLabelColor, Color.DKGRAY);
            textAppearance = types.getResourceId(R.styleable.OField_fieldTextAppearance, -1);
            labelAppearance = types.getResourceId(R.styleable.OField_fieldLabelTextAppearance, -1);
            textSize = types.getDimension(R.styleable.OField_fieldTextSize, -1);
            labelSize = types.getDimension(R.styleable.OField_fieldLabelSize, -1);
            defaultImage = types.getResourceId(R.styleable.OField_defaultImage, -1);
            types.recycle();
        }
        if (mContext.getClass().getSimpleName().contains("BridgeContext"))
            initControl();
    }

    public void useTemplate(Boolean withTemplate) {
        useTemplate = withTemplate;
    }

    private void initLayout() {
        removeAllViews();
        if (useTemplate) {
            View layout = LayoutInflater.from(mContext).inflate(
                    R.layout.base_control_template, this, false);

            if (withPadding) {
                int top_padding = layout.getPaddingTop();
                int right_padding = layout.getPaddingRight();
                int bottom_padding = layout.getPaddingBottom();
                int left_padding = layout.getPaddingLeft();
                if (!with_bottom_padding) {
                    layout.setPadding(left_padding, top_padding, right_padding, 0);
                }
                if (!with_top_padding) {
                    layout.setPadding(left_padding, 0, right_padding, bottom_padding);
                }
            } else {
                layout.setPadding(0, 0, 0, 0);
            }
            addView(layout);
            container = (ViewGroup) findViewById(R.id.control_container);
            img_icon = (ImageView) findViewById(android.R.id.icon);
            img_icon.setColorFilter(tint_color);
            setImageIcon();
        } else {
            container = this;
        }
    }

    public void initControl() {
        initLayout();
        View controlView = null;
        if (show_label) {
            label_view = getLabelView();
            container.addView(label_view);
        }
        switch (mType) {
            case Text:
                controlView = initTextControl();
                break;
            case Boolean:
                controlView = initBooleanControl();
                break;
            case Chips:
                break;
            case ManyToOne:
            case Selection:
                controlView = initSelectionWidget();
                break;
            case Date:
            case Time:
            case DateTime:
                controlView = initDateTimeControl(mType);
                break;
            case Blob:
                controlView = initBlobControl();
                break;
            default:
                return;
        }
        mControlData.setValueUpdateListener(this);
        mControlData.setEditable(getEditable());
        mControlData.initControl();
        mControlData.setValue(mValue);
        container.addView(controlView);
    }

    public <T> T getFieldView() {
        return (T) mControlData.getFieldView();
    }

    public void setIconTintColor(int color) {
        tint_color = color;
        if (img_icon != null) {
            img_icon.setColorFilter(tint_color);
        }
    }

    private void setImageIcon() {
        if (showIcon) {
            if (resId != 0)
                img_icon.setImageResource(resId);
            if (tint_color != 0)
                img_icon.setColorFilter(tint_color);
        } else
            img_icon.setVisibility(View.GONE);
    }

    public <T> void setColumn(OColumn column) {
        mColumn = column;
        mType = getType(column.getType());
        if (label_view != null) {
            label_view.setText(getLabelText());
        }
        if (mControlData != null) {
            mControlData.setColumn(mColumn);
        }
    }

    private <T> FieldType getType(Class<T> type_class) {
        try {
            // Varchar
            if (type_class.isAssignableFrom(OVarchar.class)
                    || type_class.isAssignableFrom(OInteger.class)
                    || type_class.isAssignableFrom(OFloat.class)) {
                return FieldType.Text;
            }
            // boolean
            if (type_class.isAssignableFrom(OBoolean.class)) {
                return FieldType.Boolean;
            }

            // Blob
            if (type_class.isAssignableFrom(OBlob.class)) {
                return FieldType.Blob;
            }
            // DateTime
            if (type_class.isAssignableFrom(ODateTime.class)
                    || type_class.isAssignableFrom(OTimestamp.class)) {
                return FieldType.DateTime;
            }
            // Date
            if (type_class.isAssignableFrom(ODate.class)) {
                return FieldType.Date;
            }
            // Text
            if (type_class.isAssignableFrom(OText.class)) {
                return FieldType.Text;
            }
            // FIXME: WebView type
            if (type_class.isAssignableFrom(OHtml.class)) {
                return FieldType.Text;
            }
            if (type_class.isAssignableFrom(OSelection.class)) {
                return FieldType.Selection;
            }
            // ManyToOne
            if (mColumn.getRelationType() != null
                    && mColumn.getRelationType() == OColumn.RelationType.ManyToOne) {
                return FieldType.ManyToOne;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getLabelText() {
        if (mLabel != null)
            return mLabel;
        if (mColumn != null)
            return mColumn.getLabel();
        if (mControlData != null)
            return mControlData.getLabel();
        return getFieldName();
    }

    public void setValue(Object value) {
        mValue = value;
        if (mValue != null && mControlData != null) {
            mControlData.setValue(mValue);
        }
    }

    public Object getValue() {
        if (mControlData != null)
            return mControlData.getValue();
        return null;
    }

    public void setEditable(Boolean editable) {
        mEditable = editable;
        if (mControlData != null) {
            Object value = getValue();
            mControlData.setEditable(editable);
            mControlData.initControl();
            if (value != null)
                mControlData.setValue(value);
        }
    }

    public boolean getEditable() {
        return mEditable;
    }

    public String getFieldName() {
        return mField_name;
    }

    // EditText control (TextView, EditText)
    private View initTextControl() {
        setOrientation(VERTICAL);
        OEditTextField edt = new OEditTextField(mContext);
        edt.setWidgetType(mWidgetType);
        mControlData = edt;
        edt.setResource(textSize, textAppearance, textColor);
        edt.setColumn(mColumn);
        edt.setHint(mLabel);
        return edt;
    }

    // Boolean Control (Checkbox, W-Switch)
    private View initBooleanControl() {
        OBooleanField bool = new OBooleanField(mContext);
        mControlData = bool;
        bool.setResource(textSize, textAppearance, textColor);
        bool.setColumn(mColumn);
        bool.setEditable(getEditable());
        bool.setLabelText(getLabelText());
        bool.setWidgetType(mWidgetType);
        return bool;
    }

    // Selection, Searchable, SearchableLive
    private View initSelectionWidget() {
        OSelectionField selection = new OSelectionField(mContext);
        mControlData = selection;
        selection.setResource(textSize, textAppearance, textColor);
        selection.setLabelText(getLabelText());
        selection.setModel(mModel);
        selection.setArrayResourceId(mValueArrayId);
        selection.setColumn(mColumn);
        selection.setWidgetType(mWidgetType);
        return selection;
    }

    // Datetime (dialog with date or date time)
    private View initDateTimeControl(FieldType type) {
        ODateTimeField datetime = new ODateTimeField(mContext);
        mControlData = datetime;
        datetime.setResource(textSize, textAppearance, textColor);
        datetime.setFieldType(type);
        datetime.setParsePattern(mParsePattern);
        datetime.setLabelText(getLabelText());
        datetime.setColumn(mColumn);
        return datetime;
    }

    // Blob (file contents)
    private View initBlobControl() {
        OBlobField blob = new OBlobField(mContext);
        mControlData = blob;
        blob.setDefaultImage(defaultImage);
        blob.setImageSize(mWidgetImageSize);
        blob.setLabelText(getLabelText());
        blob.setColumn(mColumn);
        blob.setWidgetType(mWidgetType);
        return blob;
    }

    private TextView getLabelView() {
        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT);
        TextView label = new TextView(mContext);
        if (labelSize > -1) {
            label.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelSize);
        }
        if (labelAppearance > -1) {
            label.setTextAppearance(mContext, labelAppearance);
        }
        label.setTextColor(labelColor);
        label.setLayoutParams(params);
        label.setGravity(Gravity.LEFT);
        label.setText(getLabelText());
        label.setAllCaps(true);
        return label;
    }

    public void setIcon(int resourceId) {
        img_icon.setImageResource(resourceId);
    }

    public void setError(String error) {
        mControlData.setError(error);
    }

    public int getIcon() {
        return resId;
    }

    public void setModel(OModel model) {
        mModel = model;
    }

    public OModel getModel() {
        return mModel;
    }

    public OColumn getColumn() {
        return mColumn;
    }

    public void resetData() {
        mControlData.resetData();
    }

    @Override
    public void onValueUpdate(Object value) {
        mValue = value;
        if (mValueUpdateListener != null) {
            mValueUpdateListener.onFieldValueChange(this, value);
        }
        if (value instanceof ODataRow) {
            mValue = ((ODataRow) value).get(OColumn.ROW_ID);
        }
        if (mEditable) {
            if (mControlData.isControlReady()) {
                ODataRow row = new ODataRow();
                if (mOnChangeCallback != null
                        || mOnDomainFilterCallbacks != null) {
                    if (!(value instanceof ODataRow)) {
                        row.put(mColumn.getName(), value);
                    } else {
                        row = (ODataRow) value;
                    }
                }
                if (mOnChangeCallback != null) {
                    mOnChangeCallback.onValueChange(row);
                }
                if (mOnDomainFilterCallbacks != null) {
                    mColumnDomain.setValue(row.getInt(OColumn.ROW_ID));
                    mOnDomainFilterCallbacks.onFieldValueChanged(mColumnDomain);
                }
            }
        }
    }

    public void setOnValueChangeListener(IOnFieldValueChangeListener listener) {
        mValueUpdateListener = listener;
    }

    @Override
    public void visibleControl(boolean isVisible) {
        if (isVisible) {
            setVisibility(View.VISIBLE);
        } else {
            setVisibility(View.GONE);
        }
    }

    /**
     * OnChange CallBack for column
     *
     * @param callback
     */
    public void setOnChangeCallbackListener(IOnChangeCallback callback) {
        mOnChangeCallback = callback;
    }

    /**
     * Domain Filters
     *
     * @param domain
     * @param callback
     */
    public void setOnFilterDomainCallBack(OColumn.ColumnDomain domain,
                                          IOnDomainFilterCallbacks callback) {
        mColumnDomain = domain;
        mOnDomainFilterCallbacks = callback;
    }

    public interface IOnFieldValueChangeListener {
        public void onFieldValueChange(OField field, Object value);
    }
}
