/**
 * Odoo, Open Source Management Solution
 * Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>)
 * <p/>
 * 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
 * <p/>
 * 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
 * <p/>
 * 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/>
 * <p/>
 * Created on 7/1/15 5:11 PM
 */
package odoo.controls;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.odoo.core.orm.ODataRow;
import com.odoo.core.orm.OM2ORecord;
import com.odoo.core.orm.OModel;
import com.odoo.core.orm.fields.OColumn;
import com.odoo.core.orm.fields.types.OSelection;
import com.odoo.core.utils.OControls;

import java.util.ArrayList;
import java.util.List;

public class OSelectionField extends LinearLayout implements IOControlData,
        AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener, RadioGroup.OnCheckedChangeListener {
    public static final String TAG = OSelectionField.class.getSimpleName();

    private Context mContext;
    private Object mValue = null;
    private Boolean mEditable = false;
    private OField.WidgetType mWidget = null;
    private Integer mResourceArray = null;
    private OColumn mCol;
    private String mLabel;
    private OModel mModel;
    private List<ODataRow> items = new ArrayList<>();
    private ValueUpdateListener mValueUpdateListener = null;
    // Controls
    private Spinner mSpinner = null;
    private SpinnerAdapter mAdapter;
    private RadioGroup mRadioGroup = null;
    private TextView txvView = null;
    private Boolean mReady = false;
    private float textSize = -1;
    private int appearance = -1;
    private int textColor = Color.BLACK;

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

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

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

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

    private void init(Context context, AttributeSet attrs, int defStyleAttr,
                      int defStyleRes) {
        mContext = context;
        if (attrs != null) {

        }
        if (mContext.getClass().getSimpleName().contains("BridgeContext"))
            initControl();
    }

    private void createRadioGroup() {
        final LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT);
        if (mRadioGroup == null) {
            mRadioGroup = new RadioGroup(mContext);
            mRadioGroup.setLayoutParams(params);
        } else {
            removeView(mRadioGroup);
        }
        mRadioGroup.removeAllViews();
        mRadioGroup.setOnCheckedChangeListener(this);
        for (ODataRow label : items) {
            RadioButton rdoBtn = new RadioButton(mContext);
            rdoBtn.setLayoutParams(params);
            rdoBtn.setText(label.getString(mModel.getDefaultNameColumn()));
            if (textSize > -1) {
                rdoBtn.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
            }
            if (appearance > -1) {
                rdoBtn.setTextAppearance(mContext, appearance);
            }
            rdoBtn.setTextColor(textColor);
            mRadioGroup.addView(rdoBtn);
        }
        addView(mRadioGroup);
    }

    @Override
    public void initControl() {
        final LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT);
        removeAllViews();
        setOrientation(VERTICAL);
        createItems();
        if (isEditable()) {
            if (mWidget != null) {
                switch (mWidget) {
                    case RadioGroup:
                        createRadioGroup();
                        return;
                    case SelectionDialog:
                        txvView = new TextView(mContext);
                        txvView.setLayoutParams(params);
                        mAdapter = new SpinnerAdapter(mContext,
                                android.R.layout.simple_list_item_1, items);
                        setOnClickListener(new OnClickListener() {

                            @Override
                            public void onClick(View v) {

                                AlertDialog dialog = createSelectionDialog(
                                        getPos(), items, params);
                                txvView.setTag(dialog);
                                dialog.show();
                            }
                        });
                        if (textSize > -1) {
                            txvView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
                        }
                        if (appearance > -1) {
                            txvView.setTextAppearance(mContext, appearance);
                        }
                        txvView.setTextColor(textColor);
                        addView(txvView);
                        return;
                    case Searchable:
                    case SearchableLive:
                        txvView = new TextView(mContext);
                        txvView.setLayoutParams(params);
                        setOnClickListener(new OnClickListener() {

                            @Override
                            public void onClick(View v) {
                                Intent intent = new Intent(mContext,
                                        SearchableItemActivity.class);
                                intent.putExtra("resource_id", mResourceArray);
                                intent.putExtra("selected_position", getPos());
                                intent.putExtra(OColumn.ROW_ID, getPos());
                                intent.putExtra("search_hint", getLabel());
                                if (mCol != null) {
                                    intent.putExtra("column_name", mCol.getName());
                                }
                            /*
                             * FIXME: What about filter domain. Pass detail for
							 * filter domain
							 */
                                intent.putExtra("model", mModel.getModelName());
                                intent.putExtra("live_search",
                                        (mWidget == OField.WidgetType.SearchableLive));
                                try {
                                    mContext.unregisterReceiver(valueReceiver);
                                } catch (Exception e) {

                                }
                                try {
                                    mContext.registerReceiver(valueReceiver,
                                            new IntentFilter("searchable_value_select"));
                                } catch (Exception e) {

                                }
                                mContext.startActivity(intent);
                            }
                        });
                        if (textSize > -1) {
                            txvView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
                        }
                        if (appearance > -1) {
                            txvView.setTextAppearance(mContext, appearance);
                        }
                        txvView.setTextColor(textColor);
                        addView(txvView);
                        return;
                    default:
                        break;
                }
            }

            // Default View
            mSpinner = new Spinner(mContext);
            mSpinner.setLayoutParams(params);
            mAdapter = new SpinnerAdapter(mContext,
                    android.R.layout.simple_list_item_1, items);
            mSpinner.setAdapter(mAdapter);
            mSpinner.setOnItemSelectedListener(this);
            addView(mSpinner);
        } else {
            setOnClickListener(null);
            txvView = new TextView(mContext);
            if (textSize > -1) {
                txvView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
            }
            if (appearance > -1) {
                txvView.setTextAppearance(mContext, appearance);
            }
            txvView.setTextColor(textColor);
            addView(txvView);
        }
    }

    private void createItems() {
        items.clear();
        if (!mContext.getClass().getSimpleName().contains("BridgeContext")) {
            if (mResourceArray != null && mResourceArray != -1) {
                String[] items_list = mContext.getResources().getStringArray(
                        mResourceArray);
                ODataRow row = new ODataRow();
                row.put(OColumn.ROW_ID, -1);
                row.put(mModel.getDefaultNameColumn(), "Nothing Selected");
                items.add(row);
                for (int i = 0; i < items_list.length; i++) {
                    row = new ODataRow();
                    row.put(OColumn.ROW_ID, i);
                    row.put(mModel.getDefaultNameColumn(), items_list[i]);
                    items.add(row);
                }
            } else if (mCol.getType().isAssignableFrom(OSelection.class)) {
                List<ODataRow> rows = new ArrayList<>();
                Object defaultVal = mCol.getDefaultValue();
                for (String key : mCol.getSelectionMap().keySet()) {
                    String val = mCol.getSelectionMap().get(key);
                    ODataRow row = new ODataRow();
                    row.put("key", key);
                    row.put("name", val);
                    if (defaultVal != null && defaultVal.toString().equals(val)) {
                        rows.add(0, row);
                    } else {
                        rows.add(row);
                    }
                }
                items.addAll(rows);
            } else {
                items.addAll(getRecordItems(mModel, mCol));
            }
        }
    }

    private int getPos() {
        if (mResourceArray != -1 && mValue != null) {
            return Integer.parseInt(mValue.toString());
        } else if (mCol.getType().isAssignableFrom(OSelection.class)) {
            if (items.size() <= 0) {
                createItems();
            }
            for (ODataRow item : items) {
                int index = items.indexOf(item);
                if (item.getString("key").equals(mValue.toString())) {
                    return index;
                }
            }
        } else {
            ODataRow rec = getValueForM2O();
            if (rec != null) {
                return rec.getInt(OColumn.ROW_ID);
            }
        }
        return -1;
    }

    @Override
    public void setValue(Object value) {
        mValue = value;
        if (mValue == null || mValue.toString().equals("false")) {
            mValue = -1;
        }
        ODataRow row = new ODataRow();
        if (isEditable()) {
            if (mWidget != null) {
                switch (mWidget) {
                    case RadioGroup:
                        if (mResourceArray != -1) {
                            ((RadioButton) mRadioGroup.getChildAt(getPos()))
                                    .setChecked(true);
                            row = items.get(getPos());
                        } else {
                            Integer row_id = null;
                            if (mValue instanceof OM2ORecord) {
                                row = ((OM2ORecord) mValue).browse();
                                row_id = row.getInt(OColumn.ROW_ID);
                            } else
                                row_id = (Integer) mValue;
                            int index = 0;
                            for (int i = 0; i < items.size(); i++) {
                                if (items.get(i).getInt(OColumn.ROW_ID) == row_id) {
                                    index = i;
                                    break;
                                }
                            }
                            row = items.get(index);
                            ((RadioButton) mRadioGroup.getChildAt(index))
                                    .setChecked(true);
                        }
                        break;
                    case Searchable:
                    case SearchableLive:
                    case SelectionDialog:
                        if (mResourceArray != -1) {
                            row = items.get(getPos());
                        } else {
                            if (mValue instanceof OM2ORecord)
                                row = ((OM2ORecord) mValue).browse();
                            else if (mValue instanceof Integer)
                                row = getRecordData((Integer) mValue);
                        }
                        txvView.setText(row.getString(mModel.getDefaultNameColumn()));
                        if (txvView.getTag() != null) {
                            AlertDialog dialog = (AlertDialog) txvView.getTag();
                            dialog.dismiss();
                        }
                        break;
                    default:
                        break;
                }
            } else {
                if (mResourceArray != -1) {
                    mSpinner.setSelection(getPos());
                    row = items.get(getPos());
                } else if (mCol.getType().isAssignableFrom(OSelection.class)) {
                    int pos = getPos();
                    mSpinner.setSelection(pos);
                } else {
                    Integer row_id = null;
                    if (mValue instanceof OM2ORecord) {
                        row = ((OM2ORecord) mValue).browse();
                        row_id = row.getInt(OColumn.ROW_ID);
                    } else if (mValue instanceof Integer)
                        row_id = (Integer) mValue;
                    int index = 0;
                    for (int i = 0; i < items.size(); i++) {
                        if (items.get(i).getInt(OColumn.ROW_ID) == row_id) {
                            index = i;
                            break;
                        }
                    }
                    row = items.get(index);
                    mSpinner.setSelection(index);
                }
            }
        } else {
            if (mResourceArray != -1 || mCol.getType().isAssignableFrom(OSelection.class)) {
                int position = getPos();
                // Ignoring if default value not set for field.
                if (position != -1)
                    row = items.get(position);
            } else {
                if (mValue instanceof OM2ORecord) {
                    row = ((OM2ORecord) mValue).browse();
                    if (row == null) {
                        row = new ODataRow();
                    }
                } else {
                    if (!(mValue instanceof Boolean) && mValue != null && !mValue.toString().equals("false")) {
                        int row_id = (Integer) mValue;
                        row = getRecordData(row_id);
                    } else {
                        row = new ODataRow();
                        row.put(mModel.getDefaultNameColumn(), "No " + mCol.getLabel() + " selected");
                    }
                }
            }
            if (!row.getString(mModel.getDefaultNameColumn()).equals("false"))
                txvView.setText(row.getString(mModel.getDefaultNameColumn()));
        }
        if (isEditable() && mValueUpdateListener != null && (mValue instanceof Integer &&
                (int) mValue != -1)) {
            mValueUpdateListener.onValueUpdate(row);
        }
    }

    @Override
    public View getFieldView() {
        return null;
    }


    @Override
    public void setError(String error) {
        if (error != null)
            Toast.makeText(mContext, error, Toast.LENGTH_LONG).show();
    }

    private ODataRow getValueForM2O() {
        if (getValue() != null) {
            if (getValue() instanceof OM2ORecord)
                return ((OM2ORecord) getValue()).browse();
            else if (getValue() instanceof Integer)
                return getRecordData((Integer) getValue());
        }
        return null;
    }

    @Override
    public Object getValue() {
        if (mValue instanceof OM2ORecord) {
            return ((OM2ORecord) mValue).getId();
        }
        return mValue;
    }

    @Override
    public void setEditable(Boolean editable) {
        mEditable = editable;
        initControl();
    }

    @Override
    public Boolean isEditable() {
        return mEditable;
    }

    public void setWidgetType(OField.WidgetType type) {
        mWidget = type;
        initControl();
    }

    public void setArrayResourceId(int res_id) {
        mResourceArray = res_id;
    }

    public void setColumn(OColumn col) {
        mCol = col;
        if (mCol != null && mLabel == null) {
            mLabel = mCol.getLabel();
        }
    }

    private ODataRow getRecordData(int row_id) {
        ODataRow row;
        if (row_id > 0) {
            OModel rel_model = mModel.createInstance(mCol.getType());
            row = rel_model.browse(row_id);
        } else {
            row = items.get(0);
        }
        return row;
    }

    private class SpinnerAdapter extends ArrayAdapter<ODataRow> {

        public SpinnerAdapter(Context context, int resource,
                              List<ODataRow> objects) {
            super(context, resource, objects);
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            return generateView(position, convertView, parent);
        }

        @Override
        public View getDropDownView(int position, View convertView,
                                    ViewGroup parent) {
            return generateView(position, convertView, parent);
        }

        private View generateView(int position, View convertView,
                                  ViewGroup parent) {
            View v = convertView;
            if (v == null)
                v = LayoutInflater.from(mContext).inflate(
                        android.R.layout.simple_list_item_1, parent, false);
            ODataRow row = getItem(position);
            OControls.setText(v, android.R.id.text1, row.getString(mModel.getDefaultNameColumn()));
            return v;
        }
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position,
                               long id) {
        if (mResourceArray != -1) {
            mValue = position;
        } else if (mCol.getType().isAssignableFrom(OSelection.class)) {
            ODataRow row = mAdapter.getItem(position);
            mValue = row.getString("key");
        } else {
            mValue = items.get(position).get(OColumn.ROW_ID);
        }
        setValue(mValue);
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        mValue = null;
    }

    @Override
    public void setLabelText(String label) {
        mLabel = label;
    }

    @Override
    public String getLabel() {
        if (mLabel != null)
            return mLabel;
        if (mCol != null)
            return mCol.getLabel();
        return "unknown";
    }

    private AlertDialog createSelectionDialog(final int selected_position,
                                              final List<ODataRow> items, LayoutParams params) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        ListView dialogView = new ListView(mContext);
        dialogView.setAdapter(mAdapter);
        dialogView.setOnItemClickListener(this);
        dialogView.setLayoutParams(params);
        builder.setView(dialogView);
        return builder.create();
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
                            long id) {
        setValue(position);
    }

    BroadcastReceiver valueReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            setValue(intent.getIntExtra("selected_position", -1));
            mContext.unregisterReceiver(valueReceiver);
        }
    };

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

    public static List<ODataRow> getRecordItems(OModel model, OColumn column) {
        List<ODataRow> items = new ArrayList<ODataRow>();

        OModel rel_model = model.createInstance(column.getType());
        StringBuffer whr = new StringBuffer();
        List<Object> args_list = new ArrayList<Object>();
        // Skipping onchange domain filter
        if (!column.hasDomainFilterColumn()) {
            for (String key : column.getDomains().keySet()) {
                OColumn.ColumnDomain domain = column.getDomains().get(key);
                if (domain.getConditionalOperator() != null) {
                    whr.append(domain.getConditionalOperator());
                } else {
                    whr.append(" ");
                    whr.append(domain.getColumn());
                    whr.append(" ");
                    whr.append(domain.getOperator());
                    whr.append(" ? ");
                    args_list.add(domain.getValue().toString());
                }
            }
        }
        String where = null;
        String[] args = null;
        if (args_list.size() > 0) {
            where = whr.toString();
            args = args_list.toArray(new String[args_list.size()]);
        }
        List<ODataRow> rows = new ArrayList<>();
        rows = rel_model.select(new String[]{rel_model.getDefaultNameColumn()}, where,
                args, rel_model.getDefaultNameColumn());
        ODataRow row = new ODataRow();
        row.put(OColumn.ROW_ID, -1);
        row.put(rel_model.getDefaultNameColumn(), "No " + column.getLabel() + " selected");
        items.add(row);
        items.addAll(rows);
        return items;
    }

    @Override
    public void setValueUpdateListener(ValueUpdateListener listener) {
        mValueUpdateListener = listener;
    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        int index = mRadioGroup.indexOfChild(group.findViewById(checkedId));
        ODataRow row = items.get(index);
        setValue(row.getInt(OColumn.ROW_ID));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mReady = true;
    }

    @Override
    public Boolean isControlReady() {
        return mReady;
    }

    @Override
    public void resetData() {
        if (isEditable()) {
            if (mWidget == null) {
                if (mAdapter != null) {
                    createItems();
                    mAdapter.notifyDataSetChanged();
                }
            } else {
                switch (mWidget) {
                    case SelectionDialog:
                        createItems();
                        break;
                    case RadioGroup:
                        createItems();
                        createRadioGroup();
                        break;
                    case Searchable:
                    case SearchableLive:
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public void setResource(float textSize, int appearance, int color) {
        this.textSize = textSize;
        this.appearance = appearance;
        this.textColor = color;
    }
}
