package com.agilex.healthcare.schedulingmanager.datalayer.fork;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import jsr166y.RecursiveTask;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.agilex.healthcare.schedulingmanager.domain.SchedulingClinic;
import com.agilex.healthcare.schedulingmanager.domain.SchedulingClinics;
import com.agilex.healthcare.schedulingmanager.domain.SchedulingSpecialty;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.queryservice.TextArray;
import com.agilex.healthcare.schedulingmanager.mdws.translator.ClinicTranslator;

/**
 * This Fork/Join task class processes the list of ClinicsWithSpecialties returned by the MDWS call
 * getClinicsWithSpecialties(). The default size for the task to split is 50.
 */
@Component
@Scope(value = "prototype")
public class ProcessClinicsWithSpecialtiesTask extends RecursiveTask<SchedulingClinics> implements BeanFactoryAware {
    private static final int THRESHOLD = 50;

    // Setter-Injection by Spring
    BeanFactory beanFactory;

    @Resource
    private ClinicTranslator clinicTranslator;

    // Document to process
    private TextArray clinicsWithSpecialtiesArray;

    // Specialty Map
    private Map<String, String> specialtyMap;

    // Site Code
    private String siteCode;

    // Start Index
    private int start;

    // End index
    private int end;

    public void init(final TextArray clinicsWithSpecialtiesArray, Map<String, String> specialtyMap, String siteCode, int start, int end) {
        this.clinicsWithSpecialtiesArray = clinicsWithSpecialtiesArray;
        this.specialtyMap = specialtyMap;
        this.siteCode = siteCode;
        this.start = start;
        this.end = end;
    }

    @Override
    public SchedulingClinics compute() {
        if (clinicsWithSpecialtiesArray != null) {
            int size = end - start;
            if (size <= THRESHOLD) {
                return clinicTranslator.translate(clinicsWithSpecialtiesArray, start, end, siteCode, specialtyMap);
            } else {
                // Split work and fork
                int mid = size / 2;

                ProcessClinicsWithSpecialtiesTask leftTask = beanFactory.getBean("processClinicsWithSpecialtiesTask", ProcessClinicsWithSpecialtiesTask.class);
                leftTask.init(clinicsWithSpecialtiesArray, specialtyMap, siteCode, start, start+mid);
                leftTask.fork();

                ProcessClinicsWithSpecialtiesTask rightTask = beanFactory.getBean("processClinicsWithSpecialtiesTask", ProcessClinicsWithSpecialtiesTask.class);
                rightTask.init(clinicsWithSpecialtiesArray, specialtyMap, siteCode, start+mid, end);
                return merge(leftTask.join(), rightTask.compute());
            }
        }
        return null;
    }

    /**
     * Merge the right and left task results.
     *
     * @param r1
     * @param r2
     * @return
     */
    private SchedulingClinics merge(final SchedulingClinics r1, final SchedulingClinics r2) {
    	r1.getSchedulingClinic().addAll(r2);
    	/*Map<String, SchedulingClinic> m1 = new HashMap<String, SchedulingClinic>() {{
            for (SchedulingClinic ss: r1.getSchedulingClinic()) {
                put(ss.getName(), ss);
            }
        }};

        Map<String, SchedulingClinic> m2 = new HashMap<String, SchedulingClinic>() {{
            for (SchedulingClinic ss: r2.getSchedulingClinic()) {
                put(ss.getName(), ss);
            }
        }};

        // Merge common categories
        for (String n1: m1.keySet()) {
            if (m2.containsKey(n1)) {
                m1.get(n1).getSpecialtyClinics().addAll(m2.get(n1).getSpecialtyClinics());
            }
        }

        // Add new categories
        Set<String> inM2Only = new HashSet<String>(m2.keySet());
        inM2Only.removeAll(m1.keySet());
        if (inM2Only.size() > 0) {
            for (String n2: inM2Only) {
                r1.add(m2.get(n2));
            }
        }*/

        return r1;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}
