/*
 * The MIT License
 *
 * Copyright 2017 Isaac Aymerich <[email protected]>.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.github.segator.proxylive.tasks;

import com.github.segator.proxylive.entity.ClientInfo;
import com.github.segator.proxylive.processor.IStreamProcessor;

import java.util.*;
import java.util.Map.Entry;
import com.github.segator.proxylive.config.ProxyLiveConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

/**
 *
 * @author Isaac Aymerich <[email protected]>
 */
@Service
public class ProcessorTasks {

    Logger logger = LoggerFactory.getLogger(ProcessorTasks.class);
    private final Map<Thread, IStreamTask> httpSourceStreamTasks;
    private final Map<Thread, IStreamTask> transcodeTasks;
    private final Map<Thread, IStreamTask> directTranscodeTasks;
    private final Map<Thread, IStreamTask> HLSTasks;
    private final Map<Thread, IStreamTask> HLSDirectTasks;

    @Autowired
    private StreamProcessorsSession streamProcessorsSession;
    @Autowired
    private ProxyLiveConfiguration config;



    public ProcessorTasks() {
        httpSourceStreamTasks = new HashMap();
        transcodeTasks = new HashMap();
        directTranscodeTasks = new HashMap();
        HLSTasks = new HashMap();
        HLSDirectTasks = new HashMap();
    }

    public synchronized void runTask(IStreamTask task) throws Exception {
        task.initializate();
        Thread thread = new Thread(task, task.getIdentifier());
        getOperationMap(task).put(thread, task);
        thread.start();
    }

    public synchronized IStreamTask getTask(IStreamTask task) {
        for (IStreamTask stTask : getOperationMap(task).values()) {
            if (stTask.equals(task)) {
                return stTask;
            }
        }
        return null;
    }

    public synchronized void killTask(IStreamTask task) {
        Thread thread = getThread(task);
        if (thread != null) {
            task.terminate();
            try {
                thread.join();
            } catch (InterruptedException ex) {
            }
            getOperationMap(task).remove(thread);
        }
    }

    private synchronized Thread getThread(IStreamTask task) {
        for (Map.Entry<Thread, IStreamTask> entry : getOperationMap(task).entrySet()) {
            if (entry.getValue().equals(task)) {
                return entry.getKey();
            }
        }
        return null;
    }

    public synchronized Map<Thread, IStreamTask> getOperationMap(Class clazz) {
        if (clazz.equals(HttpDownloaderTask.class)) {
            return httpSourceStreamTasks;
        } else if (clazz.equals(DirectTranscodeTask.class)) {
            return directTranscodeTasks;
        } else if (clazz.equals(HLSDirectTask.class)) {
            return HLSDirectTasks;
        }
        return null;
    }

    public synchronized Map<Thread, IStreamTask> getOperationMap() {
        Map<Thread, IStreamTask> allMaps = new HashMap();
        allMaps.putAll(httpSourceStreamTasks);
        allMaps.putAll(transcodeTasks);
        allMaps.putAll(directTranscodeTasks);
        allMaps.putAll(HLSTasks);
        allMaps.putAll(HLSDirectTasks);
        return allMaps;
    }

    private synchronized Map<Thread, IStreamTask> getOperationMap(IStreamTask task) {
        return getOperationMap(task.getClass());
    }

    /*@Scheduled(fixedDelay = 5*1000)//Every 30 seconds
    public void killNotLinkedTasks(){
        //Kill tasks that doens't have any consumer asigned
        Map nonHLSTasks = new HashMap(directTranscodeTasks);
        Collection onlyTasks = nonHLSTasks.values();
        nonHLSTasks.putAll(httpSourceStreamTasks);
        for (ClientInfo client : new ArrayList<>(streamProcessorsSession.getClientInfoList())) {
            for (IStreamProcessor streamProcessor : new ArrayList<>(client.getStreams())) {
                IStreamTask streamBindedTask = streamProcessor.getTask();
                if (!onlyTasks.contains(streamBindedTask)) {
                    try {
                        tasks.killTask(streamBindedTask);
                    } catch (Exception ex) {
                    }
                }
            }
        }
    }*/


    @Scheduled(fixedDelay = 5 * 1000)//Every 5 seconds
    public void killHLSStreams() {
        long now = new Date().getTime();
        Map<Thread, IStreamTask> HLSTasksFore = new HashMap(HLSDirectTasks);


        for (Entry<Thread, IStreamTask> entry : HLSTasksFore.entrySet()) {
            HLSDirectTask hlsTask = (HLSDirectTask) entry.getValue();
            if (hlsTask.getLastAccess() != null) {
                long difference = (now - hlsTask.getLastAccess()) / 1000;
                if (difference > config.getFfmpeg().getHls().getTimeout()) {
                    //Kill all stream processors using this task
                    for (ClientInfo client : new ArrayList<>(streamProcessorsSession.getClientInfoList())) {
                        for (IStreamProcessor streamProcessor : new ArrayList<>(client.getStreams())) {
                            IStreamTask streamBindedTask = streamProcessor.getTask();
                            if (streamBindedTask == hlsTask) {
                                try {
                                    streamProcessor.stop(false);
                                    streamProcessorsSession.removeClientInfo(client, streamProcessor);
                                } catch (Exception ex) {
                                }
                            }
                        }
                    }
                }
            }

        }
    }

    public void stopTask(IStreamTask iStreamTask) {
        try {
            if (iStreamTask instanceof IMultiplexerStreamer) {
                ((IMultiplexerStreamer) iStreamTask).getMultiplexer().removeAllConsumers();
            }

            killTask(iStreamTask);
            if (iStreamTask.getSourceProcessor() != null) {
                iStreamTask.getSourceProcessor().stop(false);
            }
        } catch (Exception ex) {
            logger.error("Error",ex);
        }
    }
}