import {SettingPath, defaultOutputPath, OLDSettingPath} from './paths';
import {dialog} from '@electron/remote';
import logger from "./logger";
import fs from "fs";

enum VideoFormat {
    mp4="mp4",
    mov="mov",
    webm="webm",
    avi="avi",
    mkv="mkv",
}

type JSONProfile = {
    profileName: string,
    fps: number,
    width: number,
    stickDistance: number,
    stickMode2: boolean,
    videoFormat: VideoFormat
};

type JSONSettings = {
    activeProfile: string,
    profiles: JSONProfile[],
    logs: string[],
    output: string,
    showRenderTerminal: boolean
}

const defaultSettings:JSONSettings = {
    activeProfile: "Default",
    profiles: [
        {
            profileName: "Default",
            fps: 30,
            width: 540,
            stickDistance: 5,
            stickMode2: true,
            videoFormat: VideoFormat.webm
        }
    ],
    logs: [],
    output: defaultOutputPath,
    showRenderTerminal: false
};

function catchSetting(tryFunc:()=>string, catchFunc:()=>string) {
    let val;
    try {
        val = tryFunc();
    } catch(err) {
        logger.info("Failed to get setting value. Using default value:" + String(err));
        val = catchFunc();
    }
    return val;
}

function getXMLChild(doc:Document, child:string) {
    return String(doc.getElementsByTagName(child)[0].childNodes[0].nodeValue);
}

let fetchFailed = "";
const settingList = await fetch(SettingPath).then(function(response) {
    return response.text();
}).catch(function(err) {
    logger.info(err);
    return "fileLoadFailed";
}).then(async function(data) {
    if(data === "fileLoadFailed") {
        return await fetch(OLDSettingPath).then(function(response) {
            return response.text();
        }).catch(function(err) {
            logger.info(err);
            return "fileLoadFailed";
        }).then(function(data) {
            if(data === "fileLoadFailed") {
                fetchFailed = "fileLoadFailed";
                return defaultSettings;
            }
            
            const parser = new DOMParser();
            const xmlDoc = parser.parseFromString(data, 'text/xml');
            
            const allLogs = catchSetting(function() {return (getXMLChild(xmlDoc, "log") === "None")? "":getXMLChild(xmlDoc, "log");},function() {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                return ""
            });
            const newLogs = defaultSettings.logs;
            if(allLogs !== "") {
                const allLogsList = allLogs.split("\"\"");
                allLogsList.forEach(log => {
                    if(log !== "") {
                        newLogs.push(log.replace('"', ''));
                    }
                });
            }
        
            return {
                activeProfile: defaultSettings.activeProfile,
                profiles: [
                    {
                        profileName: defaultSettings.profiles[0].profileName,
                        fps: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "fps");},function() {
                            fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                            return defaultSettings.profiles[0].fps.toString();
                        })),
                        width: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "width");},function() {
                            fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                            return defaultSettings.profiles[0].width.toString();
                        })),
                        stickDistance: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "stickDistance");},function() {
                            fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                            return defaultSettings.profiles[0].stickDistance.toString();
                        })),
                        stickMode2: catchSetting(function() {return getXMLChild(xmlDoc, "stickMode2");},function() {
                            fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                            return defaultSettings.profiles[0].stickMode2.toString();
                        }) === "true",
                        videoFormat: catchSetting(function() {return getXMLChild(xmlDoc, "videoFormat");},function() {
                            return defaultSettings.profiles[0].videoFormat.toString();
                        }) as VideoFormat as VideoFormat
                    }
                ],
                logs: newLogs,
                output: catchSetting(function() {return getXMLChild(xmlDoc, "output");},function() {
                    fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                    return defaultSettings.output;
                }),
                showRenderTerminal: defaultSettings.showRenderTerminal
            } as JSONSettings;
        });
    }
    const parsedData = JSON.parse(data);
    
    let profiles:JSONProfile[] = [];
    if(parsedData.profiles !== undefined) {
        parsedData.profiles.forEach((profile:JSONProfile) => {
            if(typeof profile.profileName !== "string") {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                profile.profileName = defaultSettings.profiles[0].profileName;
            }
            if(typeof profile.fps !== "number") {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                profile.fps = defaultSettings.profiles[0].fps;
            }
            if(typeof profile.width !== "number") {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                profile.width = defaultSettings.profiles[0].width;
            }
            if(typeof profile.stickDistance !== "number") {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                profile.stickDistance = defaultSettings.profiles[0].stickDistance;
            }
            if(typeof profile.stickMode2 !== "boolean") {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                profile.stickMode2 = defaultSettings.profiles[0].stickMode2;
            }
            if(typeof profile.videoFormat !== "string") {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                profile.videoFormat = defaultSettings.profiles[0].videoFormat;
            }
            
            profiles.push({
                profileName: profile.profileName,
                fps: profile.fps,
                width: profile.width,
                stickDistance: profile.stickDistance,
                stickMode2: profile.stickMode2,
                videoFormat: profile.videoFormat
            });
        });
    } else {
        fetchFailed = "multiSetting";
        profiles = defaultSettings.profiles;
    }
    
    const logs:string[] = [];
    if(parsedData.logs !== undefined) {
        parsedData.logs.forEach((log:string) => {
            if(typeof log === "string") {
                logs.push(log);
            }
        });
    } else {
        fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
    }
    
    return {
        activeProfile: catchSetting(function() {return parsedData.activeProfile;},function() {
            fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
            return defaultSettings.activeProfile;
        }),
        profiles,
        logs,
        output: catchSetting(function() {return parsedData.output;},function() {
            fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
            return defaultSettings.output;
        }),
        showRenderTerminal: function() {
            if(typeof parsedData.showRenderTerminal === "boolean") {
                return parsedData.showRenderTerminal;
            } else {
                fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
                return defaultSettings.showRenderTerminal;
            }
        }()
    }
});
if(fetchFailed !== "") {
    writeSettings();
}

function getProfiles() {
    return settingList.profiles.map((profile) => {
        return profile.profileName;
    });
}

function createProfile(profileName:string, clone:boolean) {
    settingList.profiles.push({
        profileName: profileName,
        fps: clone? getActiveProfile().fps:defaultSettings.profiles[0].fps,
        width: clone? getActiveProfile().width:defaultSettings.profiles[0].width,
        stickDistance: clone? getActiveProfile().stickDistance:defaultSettings.profiles[0].stickDistance,
        stickMode2: clone? getActiveProfile().stickMode2:defaultSettings.profiles[0].stickMode2,
        videoFormat: clone? getActiveProfile().videoFormat:defaultSettings.profiles[0].videoFormat
    });
    
    writeSettings();
}

function ProfileLoadDefault(reset:{fps?:boolean, width?:boolean, stickDistance?:boolean, stickMode2?:boolean, videoFormat?:boolean, all?:boolean}, profileName?:string) {
    if(profileName === undefined) {
        profileName = getActiveProfile().profileName;
    }
    
    settingList.profiles.forEach(profile => {
        if(profile.profileName === profileName) {
            if(reset.all || reset.fps) {
                profile.fps = defaultSettings.profiles[0].fps;
            }
            if(reset.all || reset.width) {
                profile.width = defaultSettings.profiles[0].width;
            }
            if(reset.all || reset.stickDistance) {
                profile.stickDistance = defaultSettings.profiles[0].stickDistance;
            }
            if(reset.all || reset.stickMode2) {
                profile.stickMode2 = defaultSettings.profiles[0].stickMode2;
            }
            if(reset.all || reset.videoFormat) {
                profile.videoFormat = defaultSettings.profiles[0].videoFormat;
            }
        }
    });
    
    writeSettings();
}

function editProfile(optiones:{profileName?:string, fps?:number, width?:number, stickDistance?:number, stickMode2?:boolean, videoFormat?:VideoFormat}, profileName?:string) {
    if(profileName === undefined) {
        profileName = getActiveProfile().profileName;
    }
    
    settingList.profiles.forEach(profile => {
        if(profile.profileName === profileName) {
            if(optiones.profileName !== undefined) {
                profile.profileName = optiones.profileName;
            }
            if(optiones.fps !== undefined) {
                profile.fps = optiones.fps;
            }
            if(optiones.width !== undefined) {
                profile.width = optiones.width;
            }
            if(optiones.stickDistance !== undefined) {
                profile.stickDistance = optiones.stickDistance;
            }
            if(optiones.stickMode2 !== undefined) {
                profile.stickMode2 = optiones.stickMode2;
            }
            if(optiones.videoFormat !== undefined) {
                profile.videoFormat = optiones.videoFormat;
            }
        }
    });
    
    writeSettings();
}

function setInOutSettings(args:{logs?:string[], output?:string}) {
    if(args.logs !== undefined) {
        settingList.logs = args.logs;
    }
    if(args.output !== undefined) {
        settingList.output = args.output;
    }
    writeSettings();
}
function getInOutSettings() {
    return {logs: settingList.logs, output: settingList.output};
}

function setShowRenderTerminal(show:boolean) {
    settingList.showRenderTerminal = show;
    writeSettings();
}
function getShowRenderTerminal() {
    return settingList.showRenderTerminal;
}

function removeProfile(profileName?:string) {
    if(profileName === undefined) {
        profileName = getActiveProfile().profileName;
    }
    
    settingList.profiles.forEach(profile => {
        if(profile.profileName === profileName) {
            settingList.profiles.splice(settingList.profiles.indexOf(profile), 1);
        }
    });
    
    writeSettings();
}

function setActiveProfile(profileName:string) {
    settingList.profiles.forEach(profile => {
        if(profile.profileName === profileName) {
            settingList.activeProfile = profile.profileName;
        }
    });
    
    writeSettings();
}

function writeSettings(settings?:JSONSettings) {
    if(settings === undefined) {
        settings = settingList;
    }
    
    fs.writeFile(SettingPath, JSON.stringify(settings), function(err) {
        if(err) {
            logger.errorMSG(String(err));
        }
    });
}

function getActiveProfile() {
    let activeProfile;
    settingList.profiles.forEach(profile => {
        if(profile.profileName === settingList.activeProfile) {
            activeProfile = profile;
        }
    });
    
    if(activeProfile === undefined) {
        activeProfile = defaultSettings.profiles[0];
        logger.error("Active profile not found, using default profile");
    }
    
    return activeProfile;
}

function importProfile(importSucces:CallableFunction) {
    dialog.showOpenDialog({
        properties: [
            "openFile"
        ],
        filters: [
            {
                name: "StickExporterTX-Profile",
                extensions: [
                    "setp"
                ]
            }
        ]
    }).then(async result => {
        if(result.filePaths.length === 1) {
            const rawData = await fs.promises.readFile(result.filePaths[0], "utf8");
            const jsonData = JSON.parse(rawData) as JSONProfile[];
            const importProfiles:JSONProfile[] = [];
            jsonData.forEach(profile => {
                importProfiles.push({
                    profileName: profile.profileName,
                    fps: profile.fps,
                    width: profile.width,
                    stickDistance: profile.stickDistance,
                    stickMode2: profile.stickMode2,
                    videoFormat: profile.videoFormat
                });
            });
            
            importProfiles.forEach(importProfile => {
                while(settingList.profiles.find(profile => profile.profileName === importProfile.profileName) !== undefined) {
                    importProfile.profileName = importProfile.profileName + " (imported)";
                }
                settingList.profiles.push(importProfile);
            });
            writeSettings();
            importSucces();
        }
    }).catch(err => {
        logger.errorMSG("Import faulty: "+err);
    });
}

function exportProfile() {
    dialog.showMessageBox({
        type: "question",
        noLink: true,
        buttons: ["This", "All", "Cancel"],
        title: "Export profile",
        message: "Do you want to export all profiles or just the active one?"
    }).then(result => {
        if(result.response === 0) {
            dialog.showSaveDialog({
                filters: [
                    {
                        name: "StickExporterTX-Profile",
                        extensions: [
                            "setp"
                        ]
                    }
                ]
            }).then(result => {
                if(result.filePath !== undefined) {
                    fs.writeFile(result.filePath, JSON.stringify([getActiveProfile()]), function(err) {
                        if(err) {
                            logger.errorMSG(String(err));
                        }
                    });
                }
            }).catch(err => {
                logger.errorMSG("Export faulty: "+err);
            });
        }
        else if(result.response === 1) {
            dialog.showSaveDialog({
                filters: [
                    {
                        name: "StickExporterTX-Profile",
                        extensions: [
                            "setp"
                        ]
                    }
                ]
            }).then(result => {
                if(result.filePath !== undefined) {
                    fs.writeFile(result.filePath, JSON.stringify(settingList.profiles), function(err) {
                        if(err) {
                            logger.errorMSG(String(err));
                        }
                    });
                }
            }).catch(err => {
                logger.errorMSG("Export faulty: "+err);
            });
        }
    }).catch(err => {
        logger.errorMSG("Export faulty: "+err);
    });
}

export {
    getProfiles,
    createProfile,
    ProfileLoadDefault,
    editProfile,
    removeProfile,
    setActiveProfile,
    getActiveProfile,
    setInOutSettings,
    getInOutSettings,
    setShowRenderTerminal,
    getShowRenderTerminal,
    importProfile,
    exportProfile,
    VideoFormat
}