package lu.tudor.santec.gecamed.billing.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Rate;
import lu.tudor.santec.gecamed.billing.ejb.session.beans.NomenclatureBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.NomenclatureInterface;
import lu.tudor.santec.gecamed.core.test.TestUtils;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateCondition;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateOperator;
import lu.tudor.santec.gecamed.core.utils.querybuilder.WhereClause;

/**
 * Utility Class to create an diff/update script for CNS Billing Codes 
 * For Laboratory Tarrifs 
 * 
 * It uses the livre-vert PDF from the CNS (converted to textfile)
 * 
 * "pdftotext -layout livre-vert-01012017.pdf" on Ubuntu Linux
 * 
 * and checks it against the current billing codes in the GECAMed DB. 
 * 
 * It creates a file with the changed/new/deleted Codes 
 * as well as an update sql script for the GECAMed Database.
 *  
 * @author hermenj
 *
 * @version
 * <br>$Log: LivrevertChecker.java,v $
 */
public class LivreVertChecker {

	public static void main(String[] args) throws Exception {
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		
		// Application Date of the new Codes / livre-vert
		Date applicationDate = df.parse("2017-01-01");

		// Livre-vert PDF File from CNS converted to .txt with "pdftotext -layout livre-vert-01012017.pdf" on Ubuntu Linux
		File inFile = new File ("/home/hermenj/livre-vert-01012017.txt");
		
		// outfile where  summary of the changes will be logged
		File outFile = new File("/home/hermenj/livre-vert-changes.txt");
		
		// outfile where the update script is generated
		File outScript = new File("/home/hermenj/livre-vert-updates.sql");
		
		// DB Key TYpe for LABO
		int keytype = 2;  
		
		// Pattern matches all lines in the livre-vert that contain Codes and Coefficients. 
		Pattern p = Pattern.compile(".*\\d\\)\\s+(.*)\\s+(L\\S+)\\s+(\\d+,\\d+)\\s+(\\d+,\\d+)");
		
		TestUtils.initJBossConnection();
		NomenclatureInterface nomen = (NomenclatureInterface) ManagerFactory.getRemote(NomenclatureBean.class);
		HashSet<String> codesFromFile = new HashSet<String>();

		BufferedReader br = null;
		BufferedWriter bw = null;
		BufferedWriter sw = null;
        try {
            br = new BufferedReader(new FileReader(inFile));
            bw = new BufferedWriter(new FileWriter(outFile));
            sw = new BufferedWriter(new FileWriter(outScript));
            sw.write("\n\n-- UPDATED RATES\n");
            StringBuffer newCodeInserts = new StringBuffer("\n\n-- NEW RATES\n");
            String line;
            Integer lastIndex = 3;
            
            // read the livre-vert line by line, if it contains a code, check it agains the DB.
            while ((line = br.readLine()) != null) {
//                System.out.println(line);
                
            	try {
            		Matcher m = p.matcher(line);
            		if (m.find()) {
            			//System.out.println(line);
            			
            			// code label from the livre-vert
            			String label = m.group(1).trim().replaceAll("\\'","''");
            			
            			// billing code from the livre-vert
            			String code = m.group(2).trim();
            			
            			// new Coefficient from the livre-vert
            			String newCoeff = m.group(3).trim().replace(',', '.');

            			// save all codes form the file
            			codesFromFile.add(code);
            			
            			// convert coeff. from file to double 
            			Double newCoeffNum = Double.parseDouble(newCoeff);
            			
            			// fetch current rate from the db.
            			Rate r = nomen.getRateByCode(code, applicationDate);
            			
            			System.out.print(code + "\t" + label + "\t");
            			bw.write(code + "\t");
            			
            			StringBuffer sb = new StringBuffer("INSERT INTO billing.rate (key_type, index_id, code, label, applicability, coefficient, cat, cac, apcm, acm) VALUES (");
            			
            			
            			if (r != null) {
            				// Code found in DB
            				lastIndex = r.getIndexId();
            				if (newCoeffNum.equals(r.getCoefficient())) {
            					// Coeff is same -> no Change!
            					System.out.println(r.getCoefficient() + " = " + newCoeff  + "");
            					bw.write(r.getCoefficient() + " = " + newCoeff  + "\n");
            				} else {
            					// Coeff changed -> Code Updated
            					System.out.println(r.getCoefficient() + " -> " + newCoeff  + "\t CHANGED");
            					bw.write(r.getCoefficient() + " -> " + newCoeff  + "\t CHANGED\n");
            					
            					sb.append(r.getKeyType()).append(", ");
            					sb.append(r.getIndexId()).append(", ");
            					sb.append("'").append(r.getCode()).append("', ");
            					sb.append("E'").append(r.getLabel().replaceAll("\\'","''")).append("', ");
            					sb.append("'").append(df.format(applicationDate)).append("', ");
            					sb.append(newCoeff).append(", ");
            					sb.append(r.getCAT()).append(", ");
            					sb.append(r.getCAC()).append(", ");
            					sb.append(r.getAPCM()).append(", ");
            					sb.append(r.getACM());
            					sb.append(");\n");
            					sw.write(sb.toString());
            				}
            			} else {
            				// Code NEW
            				System.out.println(newCoeff  + "\t NEW CODE!!");
            				bw.write(newCoeff  + "\t NEW CODE!!\n");

        					sb.append(keytype).append(", ");
        					sb.append(lastIndex).append(", ");
        					sb.append("'").append(code).append("', ");
        					sb.append("E'").append(label).append("', ");
        					sb.append("'").append(df.format(applicationDate)).append("', ");
        					sb.append(newCoeff).append(", ");
        					sb.append((label.indexOf("CAT") > 0)).append(", ");
        					sb.append((label.indexOf("CAC") > 0)).append(", ");
        					sb.append((label.indexOf("APCM") > 0)).append(", ");
        					sb.append((label.indexOf("ACM") > 0));
        					sb.append(");\n");
        					
        					newCodeInserts.append(sb.toString());
            			}	
            		} else {
//                    System.out.println("NO MATCH");
            		}
					
				} catch (Exception e) {
					e.printStackTrace();
				}
            }
            
            // write new codes to end of sql file
            sw.write(newCodeInserts.toString());
            
            
            // now we need to handle all codes that are in the DB but no longer in the livre-vert. They will be set to 0.0
            // fetch all current LABO Codes (keytype 2)
            WhereClause cl = new WhereClause();
            cl.addCondition(new HibernateCondition("keyType", HibernateOperator.c_EqualOperator, keytype));
            Collection<Rate> existingRates = nomen.getRatesByWhereClause(cl);
            System.out.println("Fetched " + existingRates.size() + " Rates from DB");
            
            sw.write("\n\n-- DELETED RATES\n");
            
            for (Rate r : existingRates) {
            	if (r.getCoefficient() == 0.0) {
            		// already "deleted" code
            		continue;
            	}
            	
            	// if code was not in the the new livre-vert -> delete it (set coeff to 0.0)
            	if (! codesFromFile.contains(r.getCode())) {
            		System.out.println(r.getCode()  + "\t DELETED CODE!!");
    				bw.write(r.getCode()  + "\t DELETED CODE!!\n");
    				
    				StringBuffer sb = new StringBuffer("INSERT INTO billing.rate (key_type, index_id, code, label, applicability, coefficient, cat, cac, apcm, acm) VALUES (");
    				sb.append(r.getKeyType()).append(", ");
					sb.append(r.getIndexId()).append(", ");
					sb.append("'").append(r.getCode()).append("', ");
					sb.append("E'").append(r.getLabel().replaceAll("\\'","''")).append("', ");
					sb.append("'").append(df.format(applicationDate)).append("', ");
					sb.append("0.0").append(", ");
					sb.append(r.getCAT()).append(", ");
					sb.append(r.getCAC()).append(", ");
					sb.append(r.getAPCM()).append(", ");
					sb.append(r.getACM());
					sb.append(");\n");
					sw.write(sb.toString());
    				
            	}
			}
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
                if (br != null) br.close();
                if (bw != null) bw.close();
                if (sw != null) sw.close();
        }
		
	}

}
