package com.chenyang.nse.bussiness.service.webApi.business;

import com.chenyang.nse.bussiness.config.WebApiConfig;
import com.chenyang.nse.bussiness.entity.webApi.ApiCode;
import com.chenyang.nse.bussiness.entity.webApi.DataParams;
import com.chenyang.nse.bussiness.entity.webApi.DataParamsList;
import com.chenyang.nse.bussiness.entity.webApi.Msg;
import com.chenyang.nse.bussiness.tools.dataprocess.ApplicationContextProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Scheduler {
   private static final Logger LOGGER = LoggerFactory.getLogger(Scheduler.class);
   private static final int MAX_TASK_THREAD_CNT = 4;
   private static final ExecutorService EXECUTOR_SERVICE;
   private DataParamsList params;
   private WebApiConfig config;

   public Scheduler(DataParamsList params) {
      this.params = params;
      this.config = (WebApiConfig)ApplicationContextProvider.getBean(WebApiConfig.class);
   }

   public Msg perform(int type, int total) {
      int taskConstructingSize = 0;
      int MAX_TASK_BATCH = this.config.getTaskBatch();
      int taskNums = total % MAX_TASK_BATCH == 0 ? total / MAX_TASK_BATCH : total / MAX_TASK_BATCH + 1;
      CountDownLatch latch = new CountDownLatch(taskNums);
      List<Future<Msg>> tasks = new ArrayList();
      TaskContext context = new TaskContext(type, latch);

      for(DataParams config : this.params.getConfigs()) {
         if (config != null) {
            String[] values = config.getValues();
            if (values != null && values.length != 0) {
               int from = 0;
               int len = values.length;

               while(len > 0) {
                  int temp = taskConstructingSize + len;
                  if (temp > MAX_TASK_BATCH) {
                     TaskData data = new TaskData();
                     data.id = config.getId();
                     data.values = config.getValues();
                     data.from = from;
                     data.size = MAX_TASK_BATCH - taskConstructingSize;
                     from += data.size;
                     taskConstructingSize += data.size;
                     len -= data.size;
                     context.datas.add(data);
                  } else {
                     TaskData data = new TaskData();
                     data.id = config.getId();
                     data.values = config.getValues();
                     data.from = from;
                     data.size = temp - taskConstructingSize;
                     taskConstructingSize += data.size;
                     len -= data.size;
                     context.datas.add(data);
                  }

                  if (taskConstructingSize >= MAX_TASK_BATCH) {
                     Task task = new Task(context);
                     Future<Msg> asyncTask = EXECUTOR_SERVICE.submit(task);
                     tasks.add(asyncTask);
                     context = new TaskContext(type, latch);
                     taskConstructingSize = 0;
                  }
               }
            }
         }
      }

      if (tasks.size() < taskNums) {
         Task task = new Task(context);
         Future<Msg> asyncTask = EXECUTOR_SERVICE.submit(task);
         tasks.add(asyncTask);
      }

      try {
         try {
            latch.await();
         } catch (Exception ex) {
            LOGGER.error(ex.getMessage(), ex);
            return Msg.error(ApiCode.decFailed, ex.getMessage());
         }

         for(Future<Msg> task : tasks) {
            try {
               Msg msg = (Msg)task.get();
               if (!msg.isSuccess()) {
                  return msg;
               }
            } catch (ExecutionException e) {
               LOGGER.error(e.getMessage(), e);
            }
         }
      } catch (InterruptedException e) {
         LOGGER.error(e.getMessage(), e);

         for(Future task : tasks) {
            task.cancel(true);
         }

         return Msg.error(ApiCode.decFailed, e.getMessage());
      }

      return Msg.succed(this.params);
   }

   static {
      EXECUTOR_SERVICE = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue());
   }
}
