package _VisualDVM.TestingSystem.Common;
import Common.CommonConstants;
import Common.Utils.Loggable;
import Common.Utils.Pair;
import Common.Utils.Utils_;
import _VisualDVM.Global;
import _VisualDVM.GlobalData.Machine.Machine;
import _VisualDVM.GlobalData.Machine.MachineType;
import _VisualDVM.GlobalData.User.User;
import _VisualDVM.Repository.EmailMessage;
import _VisualDVM.Repository.Server.ServerCode;
import _VisualDVM.TestingSystem.Common.TestingPackage.TestingPackage;
import _VisualDVM.TestingSystem.Common.TestingPackageToKill.TestingPackageToKill;
import _VisualDVM.Utils;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.util.Date;
import java.util.Vector;
public abstract class TestingPlanner<P extends TestingPackage> extends TestingClient {
    protected P testingPackage;
    protected File packageLocalWorkspace = null;
    protected String serverName = "";
    protected File supervisorHome = null;
    protected Machine machine = null;
    protected User user = null;
    //---
    public TestingPlanner() {
    }
    public TestingPlanner(String... args) {
        //---
        String machineAddress = args[0];
        int machinePort = Integer.parseInt(args[1]);
        String userName = args[2];
        String userPassword = args[3];
        String userWorkspace = args[4];
        String testingSystemRoot = args[5];
        serverName = args[6];
        supervisorHome = Utils_.getHomeDirectory(); //при инициализации это текущая папка.
        //---
        Utils_.MainLog = new Loggable() {
            @Override
            public String getLogHomePath() {
                return supervisorHome.getAbsolutePath();
            }
            @Override
            public String getLogName() {
                return Global.mode.toString();
            }
        };
        Utils_.MainLog.ClearLog();
        //--
        Utils_.setHomePath(testingSystemRoot);
        Global.CheckTestingSystemDirectories();
        //---
        machine = new Machine(machineAddress, machineAddress, machinePort, MachineType.Server);
        user = new User(userName, userPassword, userWorkspace);
        //---
        Print("machineAddress=" + Utils_.Brackets(machineAddress));
        Print("machinePort=" + Utils_.Brackets(String.valueOf(machinePort)));
        Print("userName=" + Utils_.Brackets(userName));
        Print("userPassword=" + Utils_.Brackets(userPassword));
        Print("userWorkspace=" + Utils_.Brackets(userWorkspace));
        Print("root=" + Utils_.Brackets(Utils_.getHomePath()));
        Print("serverName=" + serverName);
        Print("=====");
        //----
        Utils.createEmptyFile(CommonConstants.STARTED);
    }
    //----
    protected void UpdatePackageState(TasksPackageState state_in) throws Exception {
        testingPackage.state = state_in;
        testingPackage.ChangeDate = new Date().getTime();
        ServerCommand(ServerCode.EditObject, testingPackage);
        switch (testingPackage.state) {
            case Done:
            case DoneWithErrors:
            case Aborted:
            case CompilationExecution:
            case RunningExecution:
                EmailPackage();
                break;
        }
    }
    protected void UpdatePackageState() throws Exception {
        testingPackage.ChangeDate = new Date().getTime();
        ServerCommand(ServerCode.EditObject, testingPackage);
        switch (testingPackage.state) {
            case Done:
            case DoneWithErrors:
            case Aborted:
            case CompilationExecution:
            case RunningExecution:
                EmailPackage();
                break;
        }
    }
    void UpdatePackage() throws Exception {
        testingPackage.ChangeDate = new Date().getTime();
        ServerCommand(ServerCode.EditObject, testingPackage);
    }
    public abstract String packageDescription();
    protected void EmailPackage() throws Exception {
        if (testingPackage.needsEmail == 1) {
            EmailMessage message = new EmailMessage();
            message.subject = "Состояние пакета тестирования " + packageDescription() + " " +
                    Utils_.Brackets(testingPackage.id) + " изменилось на " + Utils_.Brackets(testingPackage.state.getDescription());
            message.text = testingPackage.description;
            Vector<String> recipients = new Vector<>();
            recipients.add(testingPackage.sender_address);
            //--
            for (String address : recipients)
                ServerCommand(ServerCode.Email, address, message);
        }
    }
    //---
    protected abstract ServerCode getActivePackagesCode();
    protected abstract ServerCode getCheckIfNeedsKillCode();
    protected abstract TasksPackageState getStateAfterStart();
    protected void InitSessionCredentials() {
    }
    protected abstract void TestsSynchronize() throws Exception;
    ;
    protected abstract void PackageWorkspaceCreation() throws Exception;
    protected void AnalyseResults() throws Exception {
        UpdatePackageState(TasksPackageState.Done);
    }
    protected abstract void PackageStart() throws Exception;
    protected abstract boolean CheckNextState() throws Exception;
    protected abstract void DownloadResults() throws Exception;
    protected abstract void Kill() throws Exception;
    protected boolean Connect() {
        return true;
    }
    protected void Disconnect() {
    }
    protected void MachineConnectionError() {
    }
    protected boolean checkIfPaused() throws Exception {
        return false;
    }
    protected void PerformPackage(TestingPackage package_in) throws Exception {
        testingPackage = (P) package_in;
        //--
        Print(testingPackage.id + ":" + testingPackage.state.getDescription());
        //--
        InitSessionCredentials();
        if (testingPackage.state.equals(TasksPackageState.Analysis)) {
            AnalyseResults();
        } else {
            try {
                if (Connect()) {
                    //проверка на убийство и паузу
                    int ptk_id = (int) ServerCommand(getCheckIfNeedsKillCode(), testingPackage.id);
                    if (ptk_id != CommonConstants.Nan) {
                        Print("package " + testingPackage.id + " NEEDS TO KILL");
                        Kill();
                        ServerCommand(ServerCode.DeleteObjectByPK, new Pair(TestingPackageToKill.class, ptk_id));
                    } else if (checkIfPaused()) {
                        UpdatePackageState(TasksPackageState.Paused);
                    } else {
                        //--
                        switch (testingPackage.state) {
                            case TestsSynchronize:
                                TestsSynchronize();
                                UpdatePackageState(TasksPackageState.PackageWorkspaceCreation);
                                break;
                            case PackageWorkspaceCreation:
                                PackageWorkspaceCreation();
                                UpdatePackageState(TasksPackageState.PackageStart);
                                break;
                            case PackageStart:
                                PackageStart();
                                testingPackage.StartDate = new Date().getTime();
                                UpdatePackageState(getStateAfterStart());
                                break;
                            case RunningEnd:
                                DownloadResults();
                                break;
                            case Paused:
                                if (!checkIfPaused() && CheckNextState())
                                    UpdatePackage();
                                break;
                            default:
                                if (CheckNextState()) UpdatePackage();
                                break;
                        }
                        //--
                    }
                } else {
                    testingPackage.connectionErrosCount++;
                    UpdatePackage();
                }
            } catch (Exception ex) {
                Print("Ошибка сеанса. Соединение будет разорвано.");
                ex.printStackTrace();
                Print(ex.getMessage());
            } finally {
                Disconnect();
            }
        }
        //--
        testingPackage.destructor();
        testingPackage = null;
        System.gc();
        //--
    }
    @Override
    public void perform() throws Exception {
        testingPackage = null;
        Vector<P> activePackages = (Vector<P>) ServerCommand(getActivePackagesCode());
        for (P activePackage : activePackages)
            PerformPackage(activePackage);
    }
    protected void Finalize(String reason) {
        Print(reason);
        File stateFile = new File(supervisorHome, CommonConstants.ABORTED);
        try {
            FileUtils.writeStringToFile(stateFile, reason);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        System.exit(0);
    }
    //---------------------------------------------
    public String getPlanner() {
        return String.join("/", user.workspace, "modules", "planner");
    }
    /*
      void CheckLocal() { загрузка результатов
        local = false;
        try {
            InetAddress address = InetAddress.getByName(machine.address);
            InetAddress localAddress = InetAddress.getByName("alex-freenas.ddns.net");
            Print("machine ip=" + Utils.Brackets(address.getHostAddress()));
            Print("server ip=" + Utils.Brackets(localAddress.getHostAddress()));
            local = localAddress.getHostAddress().equals(address.getHostAddress());
            //todo в этом случае отдельный режим без ssh
        } catch (Exception ex) {
            Global.Log.PrintException(ex);
        }
    }
    */
}
