package com.xixun.xixunplayer;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.os.RemoteException;

import com.xixun.command.reply.TaskProgressReply;
import com.xixun.util.PlayerInfo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashSet;

import gnph.util.IOs;
import gnph.util.JSList;
import gnph.util.JSMap;
import gnph.util.URLConn;

public class Server extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    PlayerInfo.Stub binder = new PlayerInfo.Stub() {
        @Override
        public String getProgramName() throws RemoteException {
            try {
                String name = null;
                if(MainActivity.ins!=null) {
                    if(MainActivity.ins.insView!=null && ! MainActivity.ins.insView.avas.isEmpty()) name = MainActivity.ins.insView.curAva().name;
                    else if(MainActivity.ins.progView!=null && ! MainActivity.ins.progView.avas.isEmpty()) name = MainActivity.ins.progView.curAva().name;
                }
                Util.println("Server getProgramName. <-"+name);
                return name;
            } catch (Exception e) {
                Util.printStackTrace(e);
                return null;
            }
        }

        @Override
        public String getVersion() throws RemoteException {
            return null;
        }

        @Override
        public void setScreenWidth(int w) throws RemoteException {

        }

        @Override
        public void setScreenHeight(int h) throws RemoteException {

        }

        @Override
        public void taskScreenshot(String cmdId) throws RemoteException {

        }

        @Override
        public void setExternalTemperature(float t) throws RemoteException {

        }

        @Override
        public void setInternalTemperature(float t) throws RemoteException {

        }

        @Override
        public void setHumidity(float h) throws RemoteException {

        }

        @Override
        public boolean forcePlayProgram(String pid) throws RemoteException {
            return false;
        }

        @Override
        public boolean finishForcePlay() throws RemoteException {
            return false;
        }

        @Override
        public String getCurProgramId() throws RemoteException {
            Util.println("Server getCurProgramId ...");
            return null;
        }

        @Override
        public void setUSBProgramPwd(String pwd) throws RemoteException {

        }

        @SuppressLint("ResourceType")
        @Override
        public String executeJosnCommand(String jsonstr) throws RemoteException {
            Util.println("Server executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"}
            String commandId = null;
            try {
                var jsonBytes = jsonstr.getBytes(StandardCharsets.UTF_8);
                var json = JSMap.from(jsonBytes);
                var _type = json.stnn("_type");
                commandId = json.stnn("id");
                if(_type.equals("PlayXixunTask") || _type.equals("PlayProgramTask")) {
                    var preDownloadURL = json.str("preDownloadURL");
                    if(preDownloadURL==null) preDownloadURL = Util.serverURL+"file/download?id=";
                    var task = json.jsmap("task");
                    JSList<JSMap> jpages = task.jslist("items");
                    int proSize = 0;
                    var needDowns = new ArrayList<NeedDowns>();
                    var hases = new HashSet<String>();
                    for(var pageMap : jpages) {
                        var _program = pageMap.jsmap("_program");
                        if(_program==null) continue;
                        proSize += _program.intg("totalSize");
                        var needDown = new NeedDowns();
                        needDown.prog = pageMap.stnn("_id");
                        needDowns.add(needDown);
                        JSList<JSMap> layers = _program.jslist("layers");
                        if(layers==null || layers.isEmpty()) continue;
                        for(int ll=layers.size()-1; ll>=0; ll--) {
                            var layer = new Prog.Layer();
                            JSList<JSMap> sources = layers.get(ll).jslist("sources");
//                            var border = layers.get(ll).jsmap("border");
//                            if(border!=null) {
//                            }
                            for(var source : sources) {
                                var type = source.stnn("_type");
                                if(type.equals("Video") || type.equals("Audio") || type.equals("Image") || type.startsWith("DigitalClock") || type.equals("Timer") || type.startsWith("Environ")) {
                                    var filename = source.str("id");
                                    if(filename==null) continue;
                                    var url = source.stnn("url");
                                    if(! url.startsWith("http")) url = preDownloadURL + filename;
                                    var file = new File(Util.programDir+"/"+filename);
                                    if(file.exists() && file.length() > 0) {
                                        proSize -= file.length();
                                        hases.add(filename);
                                    } else needDown.srcs.add(new Src(filename, url));
                                } else if(type.startsWith("MultiPng") || type.equals("SplitText")) {
                                    JSList<JSMap> imgs = source.jslist("arrayPics");
                                    if(imgs.isEmpty()) continue;
                                    for(var img : imgs) {
                                        var filename = img.str("id");
                                        if(filename==null) continue;
                                        var url = img.stnn("url");
                                        if(! url.startsWith("http")) url = preDownloadURL + filename;
                                        var file = new File(Util.programDir+"/"+filename);
                                        if(file.exists() && file.length() > 0) {
                                            proSize -= file.length();
                                            hases.add(filename);
                                        } else needDown.srcs.add(new Src(filename, url));
                                    }
                                }
                            }
                        }
                    }
                    int finalProSize = proSize;
                    String finalCommandId = commandId;
                    var notificationURL = json.str("notificationURL");
                    new Thread(()->{
                        Util.deleteFiles(finalProSize, hases);
                        for(var needDown : needDowns) {
                            int cnt = 0;
                            for(var src : needDown.srcs) {
                                try {
                                    var in = new URLConn(src.url).in();
                                    var fout = new FileOutputStream(Util.programDir+"/"+src.filename);
                                    IOs.writeCloseIn(fout, in);
                                    fout.flush();
                                    fout.getFD().sync();
                                    fout.close();
                                } catch (Exception e) {
                                    Util.printStackTrace(e);
                                }
                                cnt++;
                                var progress = cnt*100/needDown.srcs.size();
                                if(cnt != needDown.srcs.size()) {
                                    if(notificationURL==null) {
                                        var intent = new Intent("xixun.intent.action.REPLY");
                                        intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, progress, 500, 3));
                                        MainActivity.ins.sendBroadcast(intent);
                                    } else {
                                        try {
                                            new URLConn(notificationURL).timeout(5000).writeJson(new JSMap(
                                                    "commandId", finalCommandId,
                                                    "taskItemId", needDown.prog,
                                                    "progress", progress).toStr()).read();
                                        } catch (Exception e) {
                                            Util.printStackTrace(e);
                                        }
                                    }
                                }
                            }
                            if(notificationURL==null) {
                                var intent = new Intent("xixun.intent.action.REPLY");
                                intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, 100, 400, 0));
                                MainActivity.ins.sendBroadcast(intent);
                            } else {
                                try {
                                    new URLConn(notificationURL).timeout(5000).writeJson(new JSMap(
                                            "commandId", finalCommandId,
                                            "taskItemId", needDown.prog,
                                            "progress", 100).toStr()).read();
                                } catch (Exception e) {
                                    Util.printStackTrace(e);
                                }
                            }
                        }
                        MainActivity.ins.runOnUiThread(() -> MainActivity.ins.initProg(jsonBytes));
                    }).start();
                    return new JSMap(
                            "_type", "Success",
                            "result", "received command",
                            "cardId", Util.getCardId(),
                            "commandId", commandId
                    ).toString();
                } else if(_type.equals("DeleteTask")) {
                    MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile());
                    return new JSMap(
                            "_type", "DataCallback",
                            "result", "received command",
                            "cardId", Util.getCardId(),
                            "commandId", commandId
                    ).toString();
                } else if(_type.equals("PlayerStateCommand")) {
                    var descs = Util.getState(MainActivity.ins.state);
                    return new JSMap(
                            "_type", "Success",
                            "cardId", Util.getCardId(),
                            "commandId", commandId,
                            "code", MainActivity.ins.state,
                            "des_en", descs[0],
                            "des", descs[1]
                    ).toString();
                } else if(_type.equals("SetPlayerBackground")) {
                    var url = json.str("url");
                    if(url==null) new File(Util.backImgFile).delete();
                    else {
                        var fout = new FileOutputStream(Util.backImgFile);
                        IOs.write(fout, new URLConn(url).in());
                        fout.flush();
                        fout.getFD().sync();
                        fout.close();
                    }
                    MainActivity.ins.runOnUiThread(() -> {
                        MainActivity.ins.backView.cosImg = url==null ? null : BitmapFactory.decodeFile(Util.backImgFile, Util.noScaled);
                        MainActivity.ins.backView.invalidate();
                    });
                    return new JSMap(
                            "_type", "Success",
                            "cardId", Util.getCardId(),
                            "commandId", commandId
                    ).toString();
                } else if(_type.equals("GetPlayerBackground")) {
                    var backImg = new File(Util.backImgFile);
                    var img = Base64.getEncoder().encodeToString(IOs.readBytesClose(backImg.exists() ? new FileInputStream(backImg) : MainActivity.ins.getResources().openRawResource(R.drawable.back)));
                    return new JSMap(
                            "_type", "DataCallback",
                            "cardId", Util.getCardId(),
                            "commandId", commandId,
                            "img", img
                    ).toString();
                } else if(_type.equalsIgnoreCase("getProgramTask")) {
                    var task = JSMap.fromClose(new FileInputStream(Util.programDir+"/program")).jsmap("task");
                    return new JSMap(
                            "_type", "ProgramTaskCallback",
                            "cardId", Util.getCardId(),
                            "commandId", commandId,
                            "task", task
                    ).toString();
                }
                return new JSMap(
                        "_type", "Error",
                        "errorMessage", "Unknown Type: "+_type,
                        "cardId", Util.getCardId(),
                        "commandId", commandId
                ).toString();
            } catch (Exception e) {
                Util.printStackTrace(e);
                return new JSMap(
                    "_type", "Error",
                    "errorMessage", Util.toStr(e),
                    "cardId", Util.getCardId(),
                    "commandId", commandId
                ).toString();
            }
        }

        @Override
        public void pausePlayer(boolean b) throws RemoteException {

        }

        @Override
        public boolean isPause() throws RemoteException {
            return false;
        }

        @Override
        public boolean clearTasks() throws RemoteException {
            MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile());
            return true;
        }

        @Override
        public int countOfPrograms(int type) throws RemoteException {
            var cnt = MainActivity.ins!=null && MainActivity.ins.progView!=null  ?  MainActivity.ins.progView.pages.size()  :  0;
            Util.println("Server countOfPrograms. <-"+cnt);
            return cnt;
        }

        @Override
        public void playInsertTask(String pid) throws RemoteException {

        }

        @Override
        public void stopInsertTask(String pid) throws RemoteException {

        }

        @Override
        public String getProgramTask() throws RemoteException {
            try {
                return IOs.readStrClose(new FileInputStream(Util.programDir+"/program"));
            } catch (Exception e) {
                Util.printStackTrace(e);
                return Util.toStr(e);
            }
        }

        @Override
        public void setUploadLogUrl(String playLog) throws RemoteException {

        }

        @Override
        public String getUploadLogUrl() throws RemoteException {
            return null;
        }
    };

    static class Src {
        String filename;
        String url;

        public Src(String filename, String url) {
            this.filename = filename;
            this.url = url;
        }
    }
    static class NeedDowns {
        String prog;
        ArrayList<Src> srcs = new ArrayList<>();
    }
}