/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.apache.synapse.task;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Map;
import java.util.Properties;

@SuppressWarnings({"UnusedDeclaration"})
public class TaskScheduler {
    private static final Log logger = LogFactory.getLog(TaskScheduler.class.getName());

    /** This same task scheduler instance can be used by many startup controllers */
    private static final Object lock = new Object();

    private String name;

    private TaskManager taskManager;

    private boolean initialized = false;

    public TaskScheduler(String name) {
        this.name = name;
    }

    public void init(Properties properties, TaskManager taskManager) {
        synchronized (lock) {
            if (initialized) {
                logger.debug("TaskScheduler already initialized.");
                return;
            }
            initialized = false;
        }
        setTaskManager(taskManager, properties);
        start();
    }

    private boolean setTaskManager(TaskManager taskManager, Properties properties) {
        synchronized (lock) {
            if (taskManager == null) {
                logger.error("Task scheduler initialization failed. Task manager is invalid.");
                return false;
            }
            this.taskManager = taskManager;
            this.taskManager.setName(name);
            if (!this.taskManager.isInitialized()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Synapse Task Scheduler initializing task manager["
                            + taskManager.getClass().getName() +"]: Properties" + properties);
                }
                this.taskManager.init(properties);
            }
            initialized = true;
            return true;
        }
    }

    public void start() {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not start task scheduler. Task scheduler not properly initialized.");
                return;
            }
            taskManager.start();
        }
    }

    public void pauseAll() {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not pause tasks. Task scheduler not properly initialized.");
                return;
            }
            taskManager.pauseAll();
        }
    }
    
    public void resumeAll() {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not resume tasks. Task scheduler not properly initialized.");
                return;
            }
            taskManager.resumeAll();
        }
    }

    public boolean scheduleTask(TaskDescription taskDescription, Map<String,
            Object> resources, Class taskClass) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not schedule task ["+ taskDescription.getName() + "]. Task scheduler not properly initialized.");
                return false;
            }
            return taskManager.schedule(taskDescription);
        }
    }

    public boolean scheduleTask(TaskDescription taskDescription, Map<String,
            Object> resources, Class jobClass, Task task) {
        if (taskDescription == null) {
            return false;
        }
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not schedule task [" + taskDescription.getName() + "]. Task scheduler not properly initialized.");
                return false;
            }
            return taskManager.schedule(taskDescription);
        }
    }

    public boolean scheduleTask(TaskDescription taskDescription) {
        if (taskDescription == null) {
            return false;
        }
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not schedule task [" + taskDescription.getName() + "]. Task scheduler not properly initialized.");
                return false;
            }
            return taskManager.schedule(taskDescription);
        }
    }

    public void shutDown() {
        synchronized (lock) {
            if (!initialized) {
                return;
            }
            taskManager.stop();
        }
    }

    public boolean isInitialized() {
        synchronized (lock) {
            if (!initialized) {
                return false;
            }
            return taskManager.isInitialized();
        }
    }

    public boolean isTaskSchedulerInitialized() {
        synchronized (lock) {
            return initialized;
        }
    }

    public void deleteTask(String name, String group) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not delete task[" + name + "," + group + "]. Task scheduler not properly initialized.");
                return;
            }
            taskManager.delete(name + "::" + group);
        }
    }

    /**
     * Resumes a specified task.
     *
     * @param name The name of the task to resume.
     * @return {@code true} if the task was successfully resumed; {@code false} if the task
     *         scheduler is not initialized or if the task resumption fails.
     */
    public boolean resumeTask(String name) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not resume the task [" + name + "]. Task scheduler not properly initialized.");
                return false;
            }
            return taskManager.resume(name);
        }
    }

    /**
     * Pauses a specified task.
     *
     * @param name The name of the task to resume.
     * @return {@code true} if the task was successfully paused; {@code false} if the task
     *         scheduler is not initialized or if the task could not be paused.
     */
    public boolean pauseTask(String name) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not pause the task [" + name + "]. Task scheduler not properly initialized.");
                return false;
            }
            return taskManager.pause(name);
        }
    }
    
    public int getRunningTaskCount() {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not determine running task count. Task scheduler not properly initialized.");
                return -1;
            }
            return taskManager.getRunningTaskCount();
        }
    }

    public boolean isTaskAlreadyRunning(Object taskKey) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not determine task status. Task scheduler not properly initialized.");
                return false;
            }
            return taskManager.isTaskRunning(taskKey);
        }
    }

    /**
     * Checks if a task with the specified name is deactivated.
     *
     * @param name the name of the task to check.
     * @return {@code true} if the task is deactivated, {@code false} otherwise or if the scheduler is not initialized.
     */
    public boolean isTaskDeactivated(String name) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not determine task status. Task scheduler not properly initialized.");
                return false;
            }
            return taskManager.isTaskDeactivated(name);
        }
    }

    public void setTriggerFactory(Object triggerFactory) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not modify task manager. Task scheduler not properly initialized.");
                return;
            }
            taskManager.setProperty("Q_TASK_TRIGGER_FACTORY", triggerFactory);
        }
    }

    public void setJobDetailFactory(Object jobDetailFactory) {
        synchronized (lock) {
            if (!initialized) {
                logger.error("Could not modify task manager. Task scheduler not properly initialized.");
                return;
            }
            taskManager.setProperty("Q_TASK_JOB_DETAIL_FACTORY", jobDetailFactory);
        }
    }
}
