import { HTTPConfig } from '../../config/HTTPConfig';
import { CastProtocol } from '../../vo/cast/CastProtocol';
import { ScreenOrientation } from '../../vo/report/ScreenOrientation';
import { CastType } from '../../vo/cast/CastType';
import { ControllerDeviceVO } from '../../vo/device/ControllerDeviceVO';
import { CastSessionResponse } from './response/CastSessionResponse';
import { ConnectDisplayResponse } from './response/ConnectDisplayResponse';
import { EmptyResponse } from './response/EmptyResponse';
import { Result } from './response/Result';
import { JsonParser } from '../json/JsonParser';
import { SDKLog } from '../../util/SDKLog';
import { CastSlot } from '../../vo/report/CastSlot';
import { IHttpRequester } from './IHttpRequester';
import { VerifyCodeResponse } from './response/VerifyCodeResponse';
import { ControllerDeviceType } from '../../vo/device/ControllerDeviceType';
import { LoginUserResponse } from './response/LoginUserResponse';
import { AccountBindDeviceResponse } from './response/AccountBindDeviceResponse';
import { AccountBindDeviceVO } from '../../vo/account/AccountBindDeviceVO';
import { Code } from './response/Code';
import { SessionDestroyReason } from '../mqtt/message/SessionDestroyReason';

export class HttpRequestExecutor {
    private readonly TAG: string = "HttpRequestExecutor";
    private httpConfig: HTTPConfig | null;
    private readonly jsonParser: JsonParser;
    private requesterImpl: IHttpRequester | null;

    constructor() {
        this.httpConfig = null;
        this.jsonParser = new JsonParser();
        this.requesterImpl = null;
    }

    setHttpRequester(requesterImpl: IHttpRequester): void {
        this.requesterImpl = requesterImpl;
    }

    setHTTPConfig(config: HTTPConfig): void {
        this.httpConfig = config;
    }

    async connectDisplay(displayCode: number, controllerDevice: ControllerDeviceVO): Promise<Result<ConnectDisplayResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("displayCode", displayCode + "");
        requestParam.set("guid", controllerDevice.guid);
        requestParam.set("controllerDeviceType", controllerDevice.controllerDeviceType);
        requestParam.set("ip", controllerDevice.ip);
        requestParam.set("gateway", controllerDevice.gateway);
        requestParam.set("mask", controllerDevice.mask);

        let ret: string = await this.post(this.httpConfig!.getHost() + this.httpConfig!.getApiConnectDevice(), 
                this.jsonParser.mapToJson(requestParam));

        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<ConnectDisplayResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<ConnectDisplayResponse> = Result.create(temp.code, temp.msg, new ConnectDisplayResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        // displayDevice
        result.data!.displayReport.displayDevice.displayCode = temp.data!.displayReport.displayDevice.displayCode;
        result.data!.displayReport.displayDevice.displayDeviceId = temp.data!.displayReport.displayDevice.displayDeviceId;
        result.data!.displayReport.displayDevice.displayDeviceName = temp.data!.displayReport.displayDevice.displayDeviceName;
        result.data!.displayReport.displayDevice.gateway = temp.data!.displayReport.displayDevice.gateway;
        result.data!.displayReport.displayDevice.ip = temp.data!.displayReport.displayDevice.ip;
        result.data!.displayReport.displayDevice.mask = temp.data!.displayReport.displayDevice.mask;
        result.data!.displayReport.displayDevice.outIp = temp.data!.displayReport.displayDevice.outIp;
        // display support
        result.data!.displayReport.displaySupport.isSupportMirrorToDisplay = temp.data!.displayReport.displaySupport.isSupportMirrorToDisplay;
        result.data!.displayReport.displaySupport.isSupportMirrorToController = temp.data!.displayReport.displaySupport.isSupportMirrorToController;
        result.data!.displayReport.displaySupport.isSupportDisplayRemote = temp.data!.displayReport.displaySupport.isSupportDisplayRemote;
        result.data!.displayReport.displaySupport.isSupportDisplayScreenCapture = temp.data!.displayReport.displaySupport.isSupportDisplayScreenCapture;
        result.data!.displayReport.displaySupport.isSupportDisplayAppInstall = temp.data!.displayReport.displaySupport.isSupportDisplayAppInstall;
        result.data!.displayReport.displaySupport.isSupportShareImageToDisplay = temp.data!.displayReport.displaySupport.isSupportShareImageToDisplay;
        result.data!.displayReport.displaySupport.isSupportShareVideoToDisplay = temp.data!.displayReport.displaySupport.isSupportShareVideoToDisplay;
        result.data!.displayReport.displaySupport.isSupportShareDocumentToDisplay = temp.data!.displayReport.displaySupport.isSupportShareDocumentToDisplay;
        result.data!.displayReport.displaySupport.majorVersion = temp.data!.displayReport.displaySupport.majorVersion;
        result.data!.displayReport.displaySupport.minorVersion = temp.data!.displayReport.displaySupport.minorVersion;
        // displayReport
        result.data!.displayReport.castSlots = temp.data!.displayReport.castSlots.map((slot:any) => 
                new CastSlot(slot.castType, slot.used, slot.max));
        result.data!.displayReport.isCastEnable = temp.data!.displayReport.isCastEnable;
        result.data!.displayReport.isCastExclusive = temp.data!.displayReport.isCastExclusive;
        // isDisplayDeviceOnline 是后增字段，可能在已有的消息/http中不存在
        if (temp.data!.displayReport.isDisplayDeviceOnline !== undefined) {
            result.data!.displayReport.isDisplayDeviceOnline = temp.data!.displayReport.isDisplayDeviceOnline;
        } else {
            result.data!.displayReport.isDisplayDeviceOnline = true;
        };
        // screenOrientation 是后增字段，可能在已有的消息/http中不存在
        if (temp.data!.displayReport.screenOrientation !== undefined) {
            result.data!.displayReport.screenOrientation = temp.data!.displayReport.screenOrientation;
        } else {
            result.data!.displayReport.screenOrientation = ScreenOrientation.LANDSCAPE;
        };
        result.data!.outIp = temp.data!.outIp;

        SDKLog.d(this.TAG, "connectDisplay result = " + result.toString());
        return result;
    }

    async disconnectDisplay(displayCode: number, controllerDevice: ControllerDeviceVO): Promise<Result<EmptyResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("displayCode", displayCode + "");
        requestParam.set("guid", controllerDevice.guid);
        
        let ret: string = await this.delete(this.httpConfig!.getHost() + this.httpConfig!.getApiConnectDevice(), 
                this.jsonParser.mapToJson(requestParam));

        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<EmptyResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<EmptyResponse> = Result.create(temp.code, temp.msg, new EmptyResponse());
        
        return result;
    }

    async createCastSession(displayCode: number, guid: string, castType: CastType, protocol: CastProtocol,
            accessToken?: string): Promise<Result<CastSessionResponse>> {
        
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("displayCode", displayCode + "");
        requestParam.set("guid", guid);
        requestParam.set("castType", castType);
        requestParam.set("protocol", protocol);

        let ret: string = await this.post(this.httpConfig!.getHost() + this.httpConfig!.getApiCastSession(), 
                this.jsonParser.mapToJson(requestParam), accessToken);

        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<CastSessionResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<CastSessionResponse> = Result.create(temp.code, temp.msg, new CastSessionResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        result.data!.castSessionId = temp.data!.castSessionId;

        return result;
    }

    async destroyCastSession(castSessionId: number, reason?: SessionDestroyReason): Promise<Result<EmptyResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("castSessionId", castSessionId + "");
        if (reason) {
            requestParam.set("reason", reason);
        }
        
        let ret: string = await this.delete(this.httpConfig!.getHost() + this.httpConfig!.getApiCastSession(), 
                this.jsonParser.mapToJson(requestParam));

        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<EmptyResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<EmptyResponse> = Result.create(temp.code, temp.msg, new EmptyResponse());
        
        return result;
    }

    async requireVerifyCode(phoneNumber: string): Promise<Result<VerifyCodeResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("phoneNumber", phoneNumber);
        
        let ret: string = await this.get(this.httpConfig!.getHost() + this.httpConfig!.getApiVerifyCode(), 
                requestParam);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<VerifyCodeResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<VerifyCodeResponse> = Result.create(temp.code, temp.msg, new VerifyCodeResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        result.data!.isSMSSuccessSend = temp.data!.isSMSSuccessSend;
        result.data!.smsResMsg = temp.data!.smsResMsg;
        return result;
    }

    async login(phoneNumber: string, verifyCode: string, deviceType: ControllerDeviceType,
            guid: string, loginType: string): Promise<Result<LoginUserResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("phoneNumber", phoneNumber);
        requestParam.set("verifyCode", verifyCode);
        requestParam.set("controllerDeviceType", deviceType);
        requestParam.set("guid", guid);
        requestParam.set("loginType", loginType);
        let ret: string = await this.post(this.httpConfig!.getHost() + this.httpConfig!.getApiUser(), 
                this.jsonParser.mapToJson(requestParam));
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<LoginUserResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<LoginUserResponse> = Result.create(temp.code, temp.msg, new LoginUserResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        result.data!.user = temp.data!.user;
        result.data!.user.accessToken = temp.data!.user.accessToken;
        result.data!.user.controllerDeviceType = temp.data!.user.controllerDeviceType;
        result.data!.user.guid = temp.data!.user.guid;
        result.data!.user.loginTime = temp.data!.user.loginTime;
        result.data!.user.userId = temp.data!.user.userId;
        result.data!.user.account.accountId = temp.data!.user.account.accountId;
        result.data!.user.account.phoneNumber = temp.data!.user.account.phoneNumber;
        result.data!.user.account.registerTime = temp.data!.user.account.registerTime;
        result.data!.user.account.vipLevel = temp.data!.user.account.vipLevel;
        return result;
    }

    async refresh(guid: string, accessToken: string): Promise<Result<LoginUserResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("guid", guid);
        let ret: string = await this.update(this.httpConfig!.getHost() + this.httpConfig!.getApiUser(), 
                this.jsonParser.mapToJson(requestParam), accessToken);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<LoginUserResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<LoginUserResponse> = Result.create(temp.code, temp.msg, new LoginUserResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        result.data!.user = temp.data!.user;
        result.data!.user.accessToken = temp.data!.user.accessToken;
        result.data!.user.controllerDeviceType = temp.data!.user.controllerDeviceType;
        result.data!.user.guid = temp.data!.user.guid;
        result.data!.user.loginTime = temp.data!.user.loginTime;
        result.data!.user.userId = temp.data!.user.userId;
        result.data!.user.account.accountId = temp.data!.user.account.accountId;
        result.data!.user.account.phoneNumber = temp.data!.user.account.phoneNumber;
        result.data!.user.account.registerTime = temp.data!.user.account.registerTime;
        result.data!.user.account.vipLevel = temp.data!.user.account.vipLevel;
        return result;
    }

    async logout(accessToken: string): Promise<Result<EmptyResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        let ret: string = await this.delete(this.httpConfig!.getHost() + this.httpConfig!.getApiUser(), 
                this.jsonParser.mapToJson(requestParam), accessToken);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<EmptyResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<EmptyResponse> = Result.create(temp.code, temp.msg, new EmptyResponse());
        return result;
    }

    async unRegister(accessToken: string): Promise<Result<EmptyResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        let ret: string = await this.delete(this.httpConfig!.getHost() + this.httpConfig!.getApiAccount(), 
                this.jsonParser.mapToJson(requestParam), accessToken);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<EmptyResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<EmptyResponse> = Result.create(temp.code, temp.msg, new EmptyResponse());
        return result;
    }

    async getAllBoundDevices(accessToken: string): Promise<Result<AccountBindDeviceResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        let ret: string = await this.get(this.httpConfig!.getHost() + this.httpConfig!.getApiBindDevice(), 
                requestParam, accessToken);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<AccountBindDeviceResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<AccountBindDeviceResponse> = Result.create(temp.code, temp.msg, new AccountBindDeviceResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        temp.data!.boundDisplayDevices.forEach(device => {
            let accountBindDevice: AccountBindDeviceVO = new AccountBindDeviceVO();
            accountBindDevice.accountId = device.accountId;
            accountBindDevice.displayDeviceAlias = device.displayDeviceAlias;
            accountBindDevice.displayDevice.displayCode = device.displayDevice.displayCode;
            accountBindDevice.displayDevice.displayDeviceId = device.displayDevice.displayDeviceId;
            accountBindDevice.displayDevice.displayDeviceName = device.displayDevice.displayDeviceName;
            accountBindDevice.displayDevice.gateway = device.displayDevice.gateway;
            accountBindDevice.displayDevice.ip = device.displayDevice.ip;
            accountBindDevice.displayDevice.mask = device.displayDevice.mask;
            accountBindDevice.displayDevice.outIp = device.displayDevice.outIp;
            result.data!.boundDisplayDevices.push(accountBindDevice);
        });
        return result;
    }

    async bindDisplay(displayDeviceId: string, alias: string, accessToken: string): Promise<Result<AccountBindDeviceResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("displayDeviceId", displayDeviceId);
        requestParam.set("displayDeviceAlias", alias);

        let ret: string = await this.post(this.httpConfig!.getHost() + this.httpConfig!.getApiBindDevice(), 
                this.jsonParser.mapToJson(requestParam), accessToken);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<AccountBindDeviceResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<AccountBindDeviceResponse> = Result.create(temp.code, temp.msg, new AccountBindDeviceResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        temp.data!.boundDisplayDevices.forEach(device => {
            let accountBindDevice: AccountBindDeviceVO = new AccountBindDeviceVO();
            accountBindDevice.accountId = device.accountId;
            accountBindDevice.displayDeviceAlias = device.displayDeviceAlias;
            accountBindDevice.displayDevice.displayCode = device.displayDevice.displayCode;
            accountBindDevice.displayDevice.displayDeviceId = device.displayDevice.displayDeviceId;
            accountBindDevice.displayDevice.displayDeviceName = device.displayDevice.displayDeviceName;
            accountBindDevice.displayDevice.gateway = device.displayDevice.gateway;
            accountBindDevice.displayDevice.ip = device.displayDevice.ip;
            accountBindDevice.displayDevice.mask = device.displayDevice.mask;
            accountBindDevice.displayDevice.outIp = device.displayDevice.outIp;
            result.data!.boundDisplayDevices.push(accountBindDevice);
        });
        return result;
    }

    async updateBindDisplayAlias(displayDeviceId: string, alias: string, accessToken: string): Promise<Result<AccountBindDeviceResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("displayDeviceId", displayDeviceId);
        requestParam.set("displayDeviceAlias", alias);

        let ret: string = await this.update(this.httpConfig!.getHost() + this.httpConfig!.getApiBindDevice(), 
                this.jsonParser.mapToJson(requestParam), accessToken);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<AccountBindDeviceResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<AccountBindDeviceResponse> = Result.create(temp.code, temp.msg, new AccountBindDeviceResponse());
        // 如果后端返回的json中code不是ok，那么response中data就为空，就不能直接对data做取值，直接返回构造的默认值。
        if (Code.OK != temp.code) {
            return result;
        }
        temp.data!.boundDisplayDevices.forEach(device => {
            let accountBindDevice: AccountBindDeviceVO = new AccountBindDeviceVO();
            accountBindDevice.accountId = device.accountId;
            accountBindDevice.displayDeviceAlias = device.displayDeviceAlias;
            accountBindDevice.displayDevice.displayCode = device.displayDevice.displayCode;
            accountBindDevice.displayDevice.displayDeviceId = device.displayDevice.displayDeviceId;
            accountBindDevice.displayDevice.displayDeviceName = device.displayDevice.displayDeviceName;
            accountBindDevice.displayDevice.gateway = device.displayDevice.gateway;
            accountBindDevice.displayDevice.ip = device.displayDevice.ip;
            accountBindDevice.displayDevice.mask = device.displayDevice.mask;
            accountBindDevice.displayDevice.outIp = device.displayDevice.outIp;
            result.data!.boundDisplayDevices.push(accountBindDevice);
        });
        return result;
    }

    async unBindDisplay(displayDeviceId: string, accessToken: string): Promise<Result<EmptyResponse>> {
        let requestParam: Map<string, string> = new Map<string, string>();
        requestParam.set("displayDeviceId", displayDeviceId);

        let ret: string = await this.delete(this.httpConfig!.getHost() + this.httpConfig!.getApiBindDevice(), 
                this.jsonParser.mapToJson(requestParam), accessToken);
        // js的json到object转换，如果是复杂对象，会丢失类型信息，所以需要手动转换，否则无法正常使用转换后的对象中的方法。
        let temp: Result<EmptyResponse> = this.jsonParser.fromJsonToHttpResult(ret);
        let result: Result<EmptyResponse> = Result.create(temp.code, temp.msg, new EmptyResponse());
        return result;
    }

    private async get(url: string, data: Map<string, string>, token?: string): Promise<string> {
        SDKLog.d(this.TAG, "GET url: " + url);
        if (null == this.requesterImpl) {
            SDKLog.e(this.TAG, "requesterImpl is null");
            return "";
        }
        return await this.requesterImpl!.get(url, data, token);
    }

    private async post(url: string, data: string, token?: string): Promise<string> {
        SDKLog.d(this.TAG, "POST url: " + url + "; data: " + data);
        if (null == this.requesterImpl) {
            SDKLog.e(this.TAG, "requesterImpl is null");
            return "";
        }
        return await this.requesterImpl!.post(url, data, token);
    }

    private async delete(url: string, data: string, token?: string): Promise<string> {
        SDKLog.d(this.TAG, "DELETE url: " + url + "; data: " + data);
        if (null == this.requesterImpl) {
            SDKLog.e(this.TAG, "requesterImpl is null");
            return "";
        }
        return await this.requesterImpl!.delete(url, data, token);
    }

    private async update(url: string, data: string, token?: string): Promise<string> {
        SDKLog.d(this.TAG, "UPDATE url: " + url + "; data: " + data);
        if (null == this.requesterImpl) {
            SDKLog.e(this.TAG, "requesterImpl is null");
            return "";
        }
        return await this.requesterImpl!.update(url, data, token);
    }
}
