/*
 * Copyright (c) 2010-2024 Belledonne Communications SARL.
 *
 * This file is part of Linphone Java Tutorial.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package service;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Timer;
import java.util.TimerTask;

import org.linphone.core.Account;
import org.linphone.core.AccountParams;
import org.linphone.core.Address;
import org.linphone.core.AuthInfo;
import org.linphone.core.Call;
import org.linphone.core.CallParams;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory;
import org.linphone.core.GlobalState;
import org.linphone.core.LogLevel;
import org.linphone.core.LoggingService;
import org.linphone.core.VideoActivationPolicy;

import controller.ScreenController;
import javafx.application.Platform;

public class CoreService extends CoreListenerStub {
    private static CoreService instance;

    public Core core;
    public ScreenController screenController;
    public IterateRunnable iterateRunnable;
    private Timer iterateTimer;

    private CoreService() {
        Factory factory = Factory.instance();
        factory.setDataDir(".");

        LoggingService loggingService = factory.getLoggingService();
        loggingService.setLogLevel(LogLevel.Message);

        core = factory.createCore("", "", null);
        core.setAudioPort(7666);
        core.setVideoPort(9666);
        core.setUserCertificatesPath(Paths.get("").toString());

        VideoActivationPolicy videoActivationPolicy = factory.createVideoActivationPolicy();
        videoActivationPolicy.setAutomaticallyAccept(true);
        videoActivationPolicy.setAutomaticallyInitiate(false);
        core.setVideoActivationPolicy(videoActivationPolicy);
        core.setVideoCaptureEnabled(true); // TODO: core.VideoSupported()
        core.usePreviewWindow(true);
        core.setVideoDisplayFilter("MSGLXVideo");
    }

    public static CoreService instance() {
        if (instance == null) {
            instance = new CoreService();
        }
        return instance;
    }

    public void start() {
        this.core.start();
        iterateRunnable = new IterateRunnable();
        iterateTimer = new Timer();
        iterateTimer.schedule(new IterateTimerTask(), 20, 20);
    }

    public void stop() {
        core.stop();
    }

    public void login(String identity, String password) {
        Address address = Factory.instance().createAddress(identity);
        AuthInfo authInfo = Factory.instance().createAuthInfo(address.getUsername(), "", password, "", "",
                address.getDomain());
        core.addAuthInfo(authInfo);

        AccountParams accountParams = core.createAccountParams();
        accountParams.setIdentityAddress(address);
        Address serverAddress = Factory.instance().createAddress("sip:" + address.getDomain() + ";transport=tls");
        accountParams.setServerAddress(serverAddress);
        accountParams.setRegisterEnabled(true);

        Account account = core.createAccount(accountParams);
        core.addAccount(account);
        core.setDefaultAccount(account);
    }

    public void logout() {
        Account account = core.getDefaultAccount();
        if (account != null) {
            AccountParams accountParams = account.getParams().clone();
            accountParams.setRegisterEnabled(false);
            account.setParams(accountParams);
        }
    }

    // Make an outgoing call.
    public void call(String uriToCall) {
        // We create an Address object from the URI.
        // This method can create a SIP Address from a username or phone number only.
        Address address = core.interpretUrl(uriToCall);

        // Initiate an outgoing call to the given destination Address.
        core.inviteAddress(address);
    }

    public Boolean toggleCamera() {
        Call call = core.getCurrentCall();
        CallParams params = core.createCallParams(call);
        Boolean newValue = !params.isVideoEnabled();
        params.setVideoEnabled(newValue);
        call.update(params);
        return newValue;
    }

    public Boolean toggleMicrophone() {
        core.setMicEnabled(!core.isMicEnabled());
        return core.isMicEnabled();
    }

    public Boolean toggleSpeaker() {
        core.getCurrentCall().setSpeakerMuted(!core.getCurrentCall().getSpeakerMuted());
        return core.getCurrentCall().getSpeakerMuted();
    }

    public void clearCoreAfterLogout() {
        core.clearAllAuthInfo();
        core.clearAccounts();
    }

    @Override
    public void onGlobalStateChanged(Core core, GlobalState state, String message) {
        if (state == GlobalState.Off) {
            iterateTimer.cancel();
        }
    }
}

class IterateTimerTask extends TimerTask {
    @Override
    public void run() {
        Platform.runLater(CoreService.instance().iterateRunnable);
    }
}

class IterateRunnable implements Runnable {
    @Override
    public void run() {
        CoreService.instance().core.iterate();
    }

}