/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed 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 com.android.testingcamera2;

import android.content.Context;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Spinner;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class RequestControlPane extends ControlPane {
    // XML attributes

    /** Name of pane tag */
    private static final String PANE_NAME = "request_pane";

    /** Attribute: ID for pane (integer) */
    private static final String PANE_ID = "id";

    // End XML attributes

    private enum TemplateType {
        MANUAL(CameraDevice.TEMPLATE_MANUAL),
        PREVIEW(CameraDevice.TEMPLATE_PREVIEW),
        RECORD(CameraDevice.TEMPLATE_RECORD),
        STILL_CAPTURE(CameraDevice.TEMPLATE_STILL_CAPTURE),
        VIDEO_SNAPSHOT(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT),
        ZERO_SHUTTER_LAG(CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);

        private int mTemplateVal;

        TemplateType(int templateVal) {
            mTemplateVal = templateVal;
        }

        int getTemplateValue() {
            return mTemplateVal;
        }
    }

    private static int mRequestPaneIdCounter = 0;

    private final int mPaneId;

    private List<CameraControlPane> mCameraPanes;
    private final List<TargetControlPane> mTargetPanes = new ArrayList<TargetControlPane>();

    private Spinner mCameraSpinner;
    private Spinner mTemplateSpinner;
    private ListView mOutputListView;

    private CheckableListAdapter mOutputAdapter;

    /**
     * Constructor for tooling only
     */
    public RequestControlPane(Context context, AttributeSet attrs) {
        super(context, attrs, null, null);

        mPaneId = 0;
        setUpUI(context);
    }

    public RequestControlPane(TestingCamera21 tc, AttributeSet attrs, StatusListener listener) {
        super(tc, attrs, listener, tc.getPaneTracker());

        mPaneId = mRequestPaneIdCounter++;
        setUpUI(tc);
    }

    public RequestControlPane(TestingCamera21 tc, XmlPullParser configParser,
            StatusListener listener) throws XmlPullParserException, IOException {
        super(tc, null, listener, tc.getPaneTracker());

        this.setName(tc.getResources().getString(R.string.request_pane_title));

        configParser.require(XmlPullParser.START_TAG, XmlPullParser.NO_NAMESPACE, PANE_NAME);

        int paneId = getAttributeInt(configParser, PANE_ID, -1);
        if (paneId == -1) {
            mPaneId = mRequestPaneIdCounter++;
        } else {
            mPaneId = paneId;
            if (mPaneId >= mRequestPaneIdCounter) {
                mRequestPaneIdCounter = mPaneId + 1;
            }
        }

        configParser.next();
        configParser.require(XmlPullParser.END_TAG, XmlPullParser.NO_NAMESPACE, PANE_NAME);

        setUpUI(tc);
    }

    public void notifyPaneEvent(ControlPane sourcePane, PaneTracker.PaneEvent event) {
        switch (event) {
            case NEW_CAMERA_SELECTED:
                if (mCameraPanes.size() > 0
                        && sourcePane == mCameraPanes.get(mCameraSpinner.getSelectedItemPosition())) {
                    updateOutputList();
                }
                break;
            case CAMERA_CONFIGURED:
                if (mCameraPanes.size() > 0
                        && sourcePane == mCameraPanes.get(mCameraSpinner.getSelectedItemPosition())) {
                    updateOutputList();
                }
                break;
            default:
                super.notifyPaneEvent(sourcePane, event);
        }
    }

    private void setUpUI(Context context) {
        String paneName =
                String.format(Locale.US, "%s %d",
                        context.getResources().getString(R.string.request_pane_title), mPaneId);
        this.setName(paneName);

        LayoutInflater inflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        inflater.inflate(R.layout.request_pane, this);

        Button captureButton = (Button) findViewById(R.id.request_pane_capture_button);
        captureButton.setOnClickListener(mCaptureButtonListener);
        Button repeatButton = (Button) findViewById(R.id.request_pane_repeat_button);
        repeatButton.setOnClickListener(mRepeatButtonListener);

        mCameraSpinner = (Spinner) findViewById(R.id.request_pane_camera_spinner);
        mTemplateSpinner = (Spinner) findViewById(R.id.request_pane_template_spinner);
        mOutputListView = (ListView) findViewById(R.id.request_pane_output_listview);

        mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);

        mOutputAdapter = new CheckableListAdapter(context, R.layout.checkable_list_item,
                new ArrayList<CheckableListAdapter.CheckableItem>());
        mOutputListView.setAdapter(mOutputAdapter);

        String[] templateNames = new String[TemplateType.values().length];
        for (int i = 0; i < templateNames.length; i++) {
            templateNames[i] = TemplateType.values()[i].toString();
        }
        mTemplateSpinner.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.spinner_item,
                templateNames));

        mPaneTracker.addPaneListener(new CameraPanesListener());
        mCameraPanes = mPaneTracker.getPanes(CameraControlPane.class);
        updateCameraPaneList();
    }

    private class CameraPanesListener extends PaneTracker.PaneSetChangedListener<CameraControlPane> {
        public CameraPanesListener() {
            super(CameraControlPane.class);
        }

        @Override
        public void onPaneAdded(ControlPane pane) {
            mCameraPanes.add((CameraControlPane) pane);
            updateCameraPaneList();
        }

        @Override
        public void onPaneRemoved(ControlPane pane) {
            mCameraPanes.remove((CameraControlPane) pane);
            updateCameraPaneList();
        }
    }

    private AdapterView.OnItemSelectedListener mCameraSpinnerListener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            updateOutputList();
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            updateOutputList();
        }
    };

    private OnClickListener mCaptureButtonListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mCameraPanes.size() == 0) {
                TLog.e("No camera selected for request");
                return;
            }
            CameraControlPane camera = mCameraPanes.get(mCameraSpinner.getSelectedItemPosition());

            CaptureRequest request = createRequest(camera);
            if (request != null) {
                camera.capture(request);
            }
        }
    };

    private OnClickListener mRepeatButtonListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mCameraPanes.size() == 0) {
                TLog.e("No camera selected for request");
                return;
            }
            CameraControlPane camera = mCameraPanes.get(mCameraSpinner.getSelectedItemPosition());

            CaptureRequest request = createRequest(camera);
            if (request != null) {
                camera.repeat(request);
            }
        }
    };

    private CaptureRequest createRequest(CameraControlPane camera) {
        if (mTargetPanes.size() == 0) {
            TLog.e("No target(s) selected for request");
            return null;
        }


        TemplateType template = TemplateType.valueOf((String) mTemplateSpinner.getSelectedItem());
        CaptureRequest.Builder builder = camera.getRequestBuilder(template.getTemplateValue());
        // TODO: Add setting overrides

        List<Integer> targetPostions = mOutputAdapter.getCheckedPositions();
        for (int i : targetPostions) {
            TargetControlPane target = mTargetPanes.get(i);
            Surface targetSurface = target.getTargetSurfaceForCameraPane(camera.getPaneName());
            if (targetSurface == null) {
                TLog.e("Target not configured for camera");
                return null;
            }
            builder.addTarget(targetSurface);
        }

        CaptureRequest request = builder.build();
        return request;
    }

    private void updateCameraPaneList() {
        String currentSelection = (String) mCameraSpinner.getSelectedItem();
        int newSelectionIndex = 0;
        String[] cameraSpinnerItems = new String[mCameraPanes.size()];
        for (int i = 0; i < cameraSpinnerItems.length; i++) {
            cameraSpinnerItems[i] = mCameraPanes.get(i).getPaneName();
            if (cameraSpinnerItems[i].equals(currentSelection)) {
                newSelectionIndex = i;
            }
        }
        mCameraSpinner.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.spinner_item,
                cameraSpinnerItems));
        mCameraSpinner.setSelection(newSelectionIndex);

        updateOutputList();
    }

    private void updateOutputList() {
        if (mCameraPanes.size() > 0) {
            CameraControlPane currentCamera =
                    mCameraPanes.get(mCameraSpinner.getSelectedItemPosition());
            mTargetPanes.clear();
            List<TargetControlPane> newPanes = currentCamera.getCurrentConfiguredTargets();
            if (newPanes != null) {
                mTargetPanes.addAll(currentCamera.getCurrentConfiguredTargets());
            }

            String[] outputSpinnerItems = new String[mTargetPanes.size()];
            for (int i = 0; i < outputSpinnerItems.length; i++) {
                outputSpinnerItems[i] = mTargetPanes.get(i).getPaneName();
            }

            mOutputAdapter.updateItems(outputSpinnerItems);

        }
    }

}
