/**
 * Copyright 2014 Nirmata, Inc.
 *
 * Licensed 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 com.nirmata.workflow.details;

import com.nirmata.workflow.events.WorkflowEvent;
import com.nirmata.workflow.events.WorkflowListener;
import com.nirmata.workflow.events.WorkflowListenerManager;
import com.nirmata.workflow.models.RunId;
import com.nirmata.workflow.models.TaskId;
import org.apache.curator.framework.listen.Listenable;
import org.apache.curator.framework.listen.ListenerContainer;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.utils.CloseableUtils;
import java.io.IOException;

public class WorkflowListenerManagerImpl implements WorkflowListenerManager
{
    private final PathChildrenCache completedTasksCache;
    private final PathChildrenCache startedTasksCache;
    private final PathChildrenCache runsCache;
    private final ListenerContainer<WorkflowListener> listenerContainer = new ListenerContainer<>();

    public WorkflowListenerManagerImpl(WorkflowManagerImpl workflowManager)
    {
        completedTasksCache = new PathChildrenCache(workflowManager.getCurator(), ZooKeeperConstants.getCompletedTaskParentPath(), false);
        startedTasksCache = new PathChildrenCache(workflowManager.getCurator(), ZooKeeperConstants.getStartedTasksParentPath(), false);
        runsCache = new PathChildrenCache(workflowManager.getCurator(), ZooKeeperConstants.getRunParentPath(), false);
    }

    @Override
    public void start()
    {
        try
        {
            runsCache.getListenable().addListener((client, event) -> {
                RunId runId = new RunId(ZooKeeperConstants.getRunIdFromRunPath(event.getData().getPath()));
                if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED )
                {
                    postEvent(new WorkflowEvent(WorkflowEvent.EventType.RUN_STARTED, runId));
                }
                else if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED )
                {
                    postEvent(new WorkflowEvent(WorkflowEvent.EventType.RUN_UPDATED, runId));
                }
            });

            startedTasksCache.getListenable().addListener((client, event) -> {
                if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED )
                {
                    RunId runId = new RunId(ZooKeeperConstants.getRunIdFromStartedTasksPath(event.getData().getPath()));
                    TaskId taskId = new TaskId(ZooKeeperConstants.getTaskIdFromStartedTasksPath(event.getData().getPath()));
                    postEvent(new WorkflowEvent(WorkflowEvent.EventType.TASK_STARTED, runId, taskId));
                }
            });

            completedTasksCache.getListenable().addListener((client, event) -> {
                if ( event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED )
                {
                    RunId runId = new RunId(ZooKeeperConstants.getRunIdFromCompletedTasksPath(event.getData().getPath()));
                    TaskId taskId = new TaskId(ZooKeeperConstants.getTaskIdFromCompletedTasksPath(event.getData().getPath()));
                    postEvent(new WorkflowEvent(WorkflowEvent.EventType.TASK_COMPLETED, runId, taskId));
                }
            });

            runsCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
            completedTasksCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
            startedTasksCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
        }
        catch ( Exception e )
        {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void close() throws IOException
    {
        CloseableUtils.closeQuietly(runsCache);
        CloseableUtils.closeQuietly(startedTasksCache);
        CloseableUtils.closeQuietly(completedTasksCache);
    }

    @Override
    public Listenable<WorkflowListener> getListenable()
    {
        return listenerContainer;
    }

    private void postEvent(WorkflowEvent event)
    {
        listenerContainer.forEach(l -> {
            if ( l != null )
            {
                l.receiveEvent(event);
            }
            return null;
        });
    }
}