package org.firstinspires.ftc.teamcode;

import android.graphics.Bitmap;

import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.hardware.HardwareMap;

import org.firstinspires.ftc.robotcore.external.ClassFactory;
import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName;
import org.firstinspires.ftc.robotcore.external.matrices.OpenGLMatrix;
import org.firstinspires.ftc.robotcore.external.matrices.VectorF;
import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
import org.firstinspires.ftc.robotcore.external.navigation.AxesOrder;
import org.firstinspires.ftc.robotcore.external.navigation.AxesReference;
import org.firstinspires.ftc.robotcore.external.navigation.Orientation;
import org.firstinspires.ftc.robotcore.external.navigation.RelicRecoveryVuMark;
import org.firstinspires.ftc.robotcore.external.navigation.VuMarkInstanceId;
import org.firstinspires.ftc.robotcore.external.navigation.VuforiaLocalizer;
import org.firstinspires.ftc.robotcore.external.navigation.VuforiaTrackable;
import org.firstinspires.ftc.robotcore.external.navigation.VuforiaTrackableDefaultListener;
import org.firstinspires.ftc.robotcore.external.navigation.VuforiaTrackables;

import java.util.concurrent.BlockingQueue;

/**
 * This OpMode illustrates the basics of using the Vuforia engine to determine
 * the identity of Vuforia VuMarks encountered on the field. The VuForia function
 * is  packaged as utility class within the main opMmode class (inner class). The findVuMark class
 * is generic usable for any single VuMark. It could be moved out of this example to a separate
 * class or a library class.
 *
 * IMPORTANT: In order to use this OpMode, you need to obtain your own Vuforia license key as
 * is explained in FIRST sample code: ConceptVuforiaNavigation.
 *
 *  VuMark is like a bar code. It is an image that contains encoded variable information. For the
 *  Relic Recovery game, the VuMark is the image of a temple. Encoded on that image in hexagonal
 *  dots is a code indicating left, center and right. Vuforia is used to locate the image in the
 *  camera field of view and extract the code returning that to your program. FIRST included a
 *  custom enum class to display the code (also called an instance id) as text.
 *
 * VuMarks are defined by two data files created by the Vuforia Target Manager. In our case, those
 * files are provided by FIRST. The files are embedded in the robot controller program by putting
 * them in the assets directory of FtcRobotController section of the project.
 *
 * You can capture VuMarks with the robot controller phone camera or with USB webcam attached to
 * a Control Hub.
 *
 * This version also streams images from the camera device over the network connection for viewing
 * in a browser. See VuforiaCapture.java and MJpegServer.hava for more informmation.
 *
 * IMPORTANT: You need to obtain your own license key to use Vuforia. The string below with which
 * 'parameters.vuforiaLicenseKey' is initialized is for illustration only, and will not function.
 * A Vuforia 'Development' license key, can be obtained free of charge from the Vuforia developer
 * web site at https://developer.vuforia.com/license-manager.
 *
 * Vuforia license keys are always 380 characters long, and look as if they contain mostly
 * random data. As an example, here is a example of a fragment of a valid key:
 *      ... yIgIzTqZ4mWjk9wd3cZO9T1axEqzuhxoGlfOOI2dRzKS4T0hQ8kT ...
 * Once you've obtained a license key, copy the string from the Vuforia web site
 * and paste it in to your code on the next line, between the double quotes. The license key needs
 * to support external cameras to use webcams.
 */

@Autonomous(name="VuMark Id - Streaming", group ="Exercises")
//@Disabled
public class VuMarkIdentificationStreaming extends LinearOpMode
{
    VuMarkFinder        vmf;
    RelicRecoveryVuMark vuMark;

    VuforiaCapture      vuforiaCapture;
    MJpegServer         server;

    @Override
    public void runOpMode() throws InterruptedException
    {
        Bitmap  cameraImage;

        // Create an instance of VuMarkFinder using RC phone camera.
        // Here we chose the back (HiRes) camera (for greater range), but
        // for a competition robot, the front camera might be more convenient.
        //vmf = new VuMarkFinder(hardwareMap, "RelicVuMark", VuforiaLocalizer.CameraDirection.BACK, true);

        // Create an instance of VuMarkFinder using USB webcam.
        vmf = new VuMarkFinder(hardwareMap, "RelicVuMark", "Webcam 1", true);

        // Create instances of the Vuforia camera capture and image streaming server.
        vuforiaCapture = new VuforiaCapture(vmf.getLocalizer());

        server = new MJpegServer();

        telemetry.addData("Mode", "Press Play to start");
        telemetry.update();

        // Start VuForia background process looking for vumarks in camera field of view. Activate
        // before waitForStart() allows you to see camera stream on DS at INIT wait. See DS menu.
        vmf.activate();

        waitForStart();

        vuforiaCapture.startCapture();

        while (opModeIsActive())
        {
            // See if a VuMark is currently visible.
            if (vmf.findVuMark())
            {
                // Convert vumark instance id to game specific id.
                vuMark = RelicRecoveryVuMark.from(vmf.instanceId);

                telemetry.addData("VuMark", "%s visible", vuMark);

                //telemetry.addData("Pose", vmf.formatPose(vmf.pose));

                telemetry.addData("X Y Z", "X=%f  Y=%f  Z=%f", vmf.tX, vmf.tY, vmf.tZ);
            }
            else
                telemetry.addData("VuMark", "not visible");

            telemetry.update();

            // Get a camera image fromm Vuforia and post it to the streaming server.

            cameraImage = vuforiaCapture.getImage();

            server.setImage(cameraImage);

            idle();
        }

        server.stop();
    }

    /**
     * VuForia VuMark finder class.
     */
    public class VuMarkFinder
    {
        private VuforiaLocalizer    vuforia;
        private VuforiaTrackables   trackables;
        private VuforiaTrackable    template;

        private VuforiaLocalizer.Parameters parameters;

        private BlockingQueue<VuforiaLocalizer.CloseableFrame> frameQueue;

        public VuMarkInstanceId     instanceId;
        public OpenGLMatrix         pose;
        public double               tX, tY, tZ, rX, rY, rZ;

        /** Constructor for using RC phone camera.
         * Create an instance of the class.
         * @param hMap HardwareMap object.
         * @param assetName Name of the asset files containing the VuMark definition.
         * @param includeViewer True to display camera viewer on RC phone.
         * @param camera Front or Back RC phone camera choice.
         */
        public VuMarkFinder(HardwareMap hMap,
                            String assetName,
                            VuforiaLocalizer.CameraDirection camera,
                            boolean includeViewer)
        {
            /*
             * To start up Vuforia, tell it the view that we wish to use for camera monitor
             * (on the RC phone). If no camera monitor is desired, use the parameterless
             * constructor instead .
             */
            if (includeViewer)
            {
                int cameraMonitorViewId = hMap.appContext.getResources().getIdentifier("cameraMonitorViewId", "id", hMap.appContext.getPackageName());
                parameters = new VuforiaLocalizer.Parameters(cameraMonitorViewId);
            }
            else
                // OR...  Do Not Activate the Camera Monitor View, to save power
                parameters  = new VuforiaLocalizer.Parameters();

            /*
             * We also indicate which camera on the RC that we wish to use.
             */
            parameters.cameraDirection = camera;
            parameters.useExtendedTracking = false;

            initializeVuforia(assetName);
        }

        /** Constructor for using webcam on Control Hub.
         * Create an instance of the class.
         * @param hMap HardwareMap object.
         * @param assetName Name of the asset files containing the VuMark definition.
         * @param cameraName Name of webcam as configured on Control Hub.
         * @param includeViewer True to display camera viewer on DS phone.
         */
        public VuMarkFinder(HardwareMap hMap,
                            String assetName,
                            String cameraName,
                            boolean includeViewer)
        {
            /*
             * To start up Vuforia, tell it the view that we wish to use for camera monitor
             * (on the RC phone). If no camera monitor is desired, use the parameterless
             * constructor instead .
             */
            if (includeViewer)
            {
                int cameraMonitorViewId = hMap.appContext.getResources().getIdentifier("cameraMonitorViewId", "id", hMap.appContext.getPackageName());
                parameters = new VuforiaLocalizer.Parameters(cameraMonitorViewId);
            }
            else
                // OR...  Do Not Activate the Camera Monitor View, to save power
                parameters  = new VuforiaLocalizer.Parameters();

            /*
             * Retrieve and set the USB web camera we are to use.
             */
            parameters.cameraName = hMap.get(WebcamName.class, cameraName);
            parameters.useExtendedTracking = false;

            initializeVuforia(assetName);
        }

        private void initializeVuforia(String assetName)
        {
            parameters.vuforiaLicenseKey = "AaDAvEH/////AAABmT7XsefufE2DuYYnYCxrBsQQ9FrK/39uullPQn7b/XVUOAU9eFLRcRYYm1JY0ChQpml/x1CPv5kyBtc5rwVrTM0I2/VcBKiulYWzGM8kZDYAIwIwpncnYbyxCHgN80KAZplNqMiL0lWP1SKFE1jXojLSu33a+gcyDvRQCJtHteF976mcXTsadxZCJFhUGx198hOmuK5HNTwjvNoxcEUmF5BOS9hLDBCrZnfTHYbYyKoKMX17a3K7FR+T8C8s+zOGvKXc9vtjNTJDUZ0D1gvyDSlzu52fHXAVTb7HRJN9rapGZ6wyqn2UGay5dxjCOFsxsWZFSzPRN3zZri//WalFQysr6MWUEykjDJhGKZucvBHr";

            vuforia = ClassFactory.getInstance().createVuforia(parameters);

            /*
              Load the data set containing the VuMarks for Relic Recovery. There's only one trackable
              in this data set: all three of the VuMarks in the game were created from this one template,
              but differ in their instance id information.
             */
            trackables = vuforia.loadTrackablesFromAsset(assetName);
            template = trackables.get(0);
            template.setName(assetName); // can help in debugging; otherwise not necessary
        }

        /**
         * Activate VuForia image processing. Call after waitForStart().
         */
        public void activate()
        {
            trackables.activate();
        }

        /**
         * Get the VuforiaLocalizer used by this class.
         * @return Localizer.
         */
        public VuforiaLocalizer getLocalizer()
        {
            return vuforia;
        }

        /**
         * Call to find out if VuMark is visible to the phone camera.
         * @return True if VuMark found, false if not.
         */
        public boolean findVuMark()
        {
            // See if any instances of the template are currently visible.
            instanceId = ((VuforiaTrackableDefaultListener) template.getListener()).getVuMarkInstanceId();

            if (instanceId != null)
            {
                // Get and display pose information, that is, vumark location relative to camera
                // center of view.

                if (vuforia.getCameraName().isWebcam())
                    pose = ((VuforiaTrackableDefaultListener) template.getListener()).getFtcCameraFromTarget();
                else
                    pose = ((VuforiaTrackableDefaultListener) template.getListener()).getPose();

                if (pose != null)
                {
                    VectorF trans = pose.getTranslation();
                    Orientation rot = Orientation.getOrientation(pose, AxesReference.EXTRINSIC, AxesOrder.XYZ, AngleUnit.DEGREES);

                    // Extract the X, Y, and Z components of the offset of the target relative to the robot
                    tX = trans.get(0);
                    tY = trans.get(1);
                    tZ = trans.get(2);

                    // Extract the rotational components of the target relative to the robot
                    rX = rot.firstAngle;
                    rY = rot.secondAngle;
                    rZ = rot.thirdAngle;
                }

                return true;
            }
            else
            {
                pose = null;
                return false;
            }
        }

        /**
         * Format pose object for human viewing.
         * @param pose Pose object returned when VuMark is found.
         * @return Pose description.
         */
        protected String formatPose(OpenGLMatrix pose)
        {
            return (pose != null) ? pose.formatAsTransform() : "null";
        }
    }
}