package ru.ximc;
import ru.ximc.libximc.*;
import java.util.Scanner;
import java.io.File;


class TestJava
{
    private static Scanner scanner = new Scanner(System.in);

    public static void print_status(JXimc.status_t status) {
        System.out.println("Status:");
        
        System.out.printf(" rpm: %d,", status.CurSpeed);
        System.out.printf(" pos: %d,", status.CurPosition);
        System.out.printf(" upwr: %d,", status.Upwr);
        System.out.printf(" ipwr: %d,", status.Ipwr);
        System.out.printf(" flags: %d", status.Flags);
        System.out.printf(" mvsts: %d", status.MvCmdSts);
        if ((status.Flags & JXimc.Flags.STATE_ALARM) != 0) {
            System.out.print(", ALARM");
        }

        if ((status.Flags & JXimc.Flags.STATE_ERRC) != 0) {
            System.out.print(", ERRC");
        }

        if ((status.Flags & JXimc.Flags.STATE_ERRD) != 0) {
            System.out.print(", ERRD");
        }

        System.out.print("\n");
    }
    
    public static String error_string(int result) {
        switch (result) {
            case -1:
                return "error";
            case -2:
                return "not implemented";
            case -4:
                return "no device";
            case -3:
                return "value error";
            default:
                return "success";
        }
    }

    private static void get_status_and_print(int device) {
        JXimc.status_t status = JXimc.get_status(device);
        print_status(status);
    }

    public static void print_status_calb(JXimc.status_calb_t status) {
        System.out.println("Status calb:");
        System.out.printf(" rpm: %f,", status.CurSpeed);
        System.out.printf(" pos: %f,", status.CurPosition);
        System.out.printf(" upwr: %d,", status.Upwr);
        System.out.printf(" ipwr: %d,", status.Ipwr);
        System.out.printf(" flags: %d,", status.Flags);
        System.out.printf(" mvsts: %d", status.MvCmdSts);
        if ((status.Flags & JXimc.Flags.STATE_ALARM) != 0) {
            System.out.print(", ALARM");
        }
        
        if ((status.Flags & JXimc.Flags.STATE_ERRC) != 0) {
            System.out.print(", ERRC");
        }
        
        if ((status.Flags & JXimc.Flags.STATE_ERRD) != 0) {
            System.out.print(", ERRD");
        }

        System.out.print("\n");
    }
    
    public static long search_devices(int[] device_count_out) {
        System.out.println("\nSearching for devices...");
        int probe_flags = (int)(JXimc.Flags.ENUMERATE_PROBE | JXimc.Flags.ENUMERATE_NETWORK);
        String enumerate_hints = "addr=";
        long devenum = JXimc.enumerate_devices(probe_flags, enumerate_hints);
        if (devenum == 0) {
            System.out.println("Error searching for devices");
            device_count_out[0] = 0;
            return 0;
        }

        device_count_out[0] = JXimc.get_device_count(devenum);
        if (device_count_out[0] < 1) {
            System.out.println("No devices found");
            return devenum;
        }

        System.out.printf("Found %d devices\n", device_count_out[0] + 1);
        return devenum;
    }

    public static void print_device_list(long devenum, int device_count) {
        System.out.println("\nFound devices list:");
        int i = 0;
        for (i = 0; i < device_count; ++i) {
            String device_name = JXimc.get_device_name(devenum, i);
            System.out.printf(" #%d. %s\n", i + 1, device_name);
        }
        System.out.printf(" #%d. virtual\n", i + 1);
    }

    public static String create_virtual_device_name() {
        String home_path = System.getProperty("user.home");
        String file_path = new File(home_path, "virt_controller.bin").getAbsolutePath();
        String device_name = "xi-emu:///" + file_path;
        System.out.printf("Virtual controller file path: '%s'\n", file_path);
        return device_name;
    }

    public static String get_device_name_from_list(long devenum, int device_count) {
        while (true) {
            print_device_list(devenum, device_count);
            System.out.println("\nWhen using an Ethernet (TCP protocol) connection, multi-user interaction with the device is possible. In this case, the same device may be controlled simultaneously from multiple sources, which can potentially lead to control conflicts.");
            System.out.println("Enter device number from the list:");
            String input = scanner.nextLine();
            int number;
            try {
                number = Integer.parseInt(input);
            } catch (NumberFormatException e) {
                continue;
            }

            if (1 <= number && number <= device_count) {
                String device_name = JXimc.get_device_name(devenum, number - 1);
                System.out.printf("Selected device: '%s'\n", device_name);
                return device_name;
            }

            if (number == device_count + 1) {
                return create_virtual_device_name();
            }
        }
    }

    public static String choose_device() {
        int[] device_count_arr = new int[1];
        long devenum = search_devices(device_count_arr);
        int device_count = device_count_arr[0];
        String device_name;
        if (device_count < 1) {
            System.out.println("The real controller is not found or busy with another app.");
            System.out.println("The virtual controller is opening to check the operation of the library...");
            device_name = create_virtual_device_name();
        } else {
            device_name = get_device_name_from_list(devenum, device_count);
        }

        if (devenum != 0) {
            JXimc.free_enumerate_devices(devenum);
        }
        return device_name;
    }
    
    public static void print_motor_type(int device) {
        /*
        Reading motor parameters and output its type.
        MotorType = 1: step.
        MotorType = 2: DC.
        MotorType = 3: BLDC.
        */

        JXimc.motor_settings_t motor_settings = JXimc.get_motor_settings(device);
        String motor_type;
        switch (motor_settings.MotorType) {
            case 1:
                motor_type = "step";
                break;
            case 2:
                motor_type = "DC";
                break;
            case 3:
                motor_type = "BLDC";
                break;
            default:
                motor_type = "motor not defined or not connected";
                break;
        }
        System.out.printf("Motor type: %d (%s)\n", motor_settings.MotorType, motor_type);
    }

	public static void main(String args[])
	{
		try {
			String jniName = System.getProperty("os.name").toLowerCase().startsWith("windows") ? "libjximc" : "jximc";
			System.loadLibrary(jniName);
            
			System.out.printf("This is a ximc test program\nlibximc version %s\n", JXimc.ximc_version());
			
            String deviceName = choose_device();
            System.out.println("\nOpening device...");
            int device = JXimc.open_device(deviceName);
            if (device == JXimc.DEVICE_UNDEFINED) {
                System.out.println("Error opening device\nPress Enter to exit...");
                new Scanner(System.in).nextLine();
                return;
            }

            // Read device status
            get_status_and_print(device);

            // Read device information
            JXimc.device_information_t di = JXimc.get_device_information(device);
            System.out.printf("Device information:\n manufacturer: %s, id: %s, product: %s, version: %d.%d.%d\n",
                di.Manufacturer, di.ManufacturerId, di.ProductDescription, di.Major, di.Minor, di.Release);

            // Send "zero" command to device
            JXimc.command_zero(device);

            // Read device status
            get_status_and_print(device);

            // Read engine settings
            JXimc.engine_settings_t engineSettings = JXimc.get_engine_settings(device);
            System.out.printf("Engine settings:\n voltage: %d, current: %d, speed %d\n",
                engineSettings.NomVoltage, engineSettings.NomCurrent, engineSettings.NomSpeed);
            
            // Read electromechanical settings
            JXimc.emf_settings_t emfSettings = JXimc.get_emf_settings(device);
            System.out.printf("Electromechanical settings:\n Km: %f, L: %f, R: %f\n",
                emfSettings.Km, emfSettings.L, emfSettings.R);

            // Read engine advanced settings
            JXimc.engine_advanced_setup_t easSettings = JXimc.get_engine_advanced_setup(device);
            System.out.printf("Engine advanced settings:\n 1 %d, 2 %d, 3 %d\n",
                easSettings.stepcloseloop_Kw, easSettings.stepcloseloop_Kp_low, easSettings.stepcloseloop_Kp_high);

            print_motor_type(device);

            // Read engine calb settings
            JXimc.calibration_t calibration = new JXimc.calibration_t();
            calibration.A = 1;
            calibration.MicrostepMode = (int)JXimc.Flags.MICROSTEP_MODE_FULL;
            JXimc.engine_settings_calb_t engine_settings_calb = JXimc.get_engine_settings_calb(device, calibration);
            System.out.printf("Engine calb settings:\n voltage: %d, current: %d, speed: %.3f\n",
                engine_settings_calb.NomVoltage, engine_settings_calb.NomCurrent, engine_settings_calb.NomSpeed);

            int sec = 2;
            System.out.printf("\nEngine rotating left for %d seconds...\n\n", sec);
            JXimc.command_left(device);
            try {
                Thread.sleep(sec * 1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }

            // Read device status
            get_status_and_print(device);

            // Read position in user units
            JXimc.get_position_calb_t get_pos = JXimc.get_position_calb(device, calibration);
            System.out.printf("Position in user units:%n pos: %.3f\n", get_pos.Position);
            
            // Read device calibrated status
            JXimc.status_calb_t status_calb = JXimc.get_status_calb(device, calibration);
            print_status_calb(status_calb);

            System.out.println("\nStopping engine...\n");
            JXimc.command_stop(device);

            // Read device status
            get_status_and_print(device);
            
            System.out.printf("\nSleeping for %d seconds...\n\n", sec);
            try {
                Thread.sleep(sec * 1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            
            JXimc.close_device(device);
		} catch (RuntimeException e) {
			System.err.println("XIMC failed with " + e.toString());
			e.printStackTrace();
		}

		System.out.println("Done");
	}
}
