package kz.cafe.client;

import java.io.*;
import java.security.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.zip.*;
import javax.ws.rs.core.*;
import kz.cafe.constants.*;
import kz.cafe.db.*;
import kz.cafe.db.logs.*;
import kz.cafe.db.logs.model.*;
import kz.cafe.db.model.*;
import kz.cafe.exceptions.*;
import kz.cafe.security.*;
import kz.cafe.statistics.*;
import org.apache.commons.logging.*;
import com.google.code.morphia.query.*;


public abstract class FEAbstractService{
   private Log log = LogFactory.getLog(FEAbstractService.class);

   private final String LOG;

   protected ServiceRequestModel srm;


   protected long lStartTime = System.currentTimeMillis();


   protected int UID;




   public FEAbstractService(ServiceRequestModel srm, String LOG){

      long lThreadID = MongoService.getUniqueRequestID();

      this.srm = srm;

      this.LOG = LOG;

      Thread.currentThread().setName("" + lThreadID);

      log.info(LOG + "\n" + LogConstants.LOG_NEW_REQUEST + "Incoming Request from: " + srm.remoteAddress);
      
      updateStatistic(MAbstractStatistics.F_TOTAL);
   }








   protected void responseTime(JRAbstractResponse response){
      responseTime(response, -1L, null, true);
   }
//   protected void responseTime(JRAbstractResponse response, UserSession3 session3){
//      responseTime(response, getUID(session3), getSID(session3), true);
//   }
//   protected void responseTime(JRAbstractResponse response, UserSession4 session4){
//      responseTime(response, getUID(session4), getSID(session4), true);
//   }





//   protected void responseTime(JRAbstractResponse response,
//                               UserSession3 session3,
//                               boolean bCompress){
//      responseTime(response, session3.profile.UID, session3.SID, bCompress);
//   }
//   protected void responseTime(JRAbstractResponse response,
//                               UserSession4 session4,
//                               boolean bCompress){
//      responseTime(response, session4.profile.UID, session4.SID, bCompress);
//   }





//   protected void responseTime(JRAbstractRequest jrAbstractRequest,
//                               JRAbstractResponse response,
//                               UserSession4 session4){
//
//      responseTime(jrAbstractRequest.toString(), response.toString(), getUID(session4), getSID(session4), true);
//   }
//   protected void responseTime(String POST,
//                               JRAbstractResponse response,
//                               UserSession4 session4){
//      responseTime(POST, response.toString(), getUID(session4), getSID(session4), true);
//   }
//   protected void responseTime(JRAbstractRequest jrAbstractRequest,
//                               String sResponse,
//                               UserSession4 session4){
//      responseTime(jrAbstractRequest.toString(), sResponse, getUID(session4), getSID(session4), true);
//   }







   protected void responseTime(JRAbstractResponse response, Long UID, String SID, boolean bCompress){
      responseTime(null, response.toString(), UID, SID, bCompress);
   }




   protected void responseTime(String sResponse, Long UID){
      responseTime(null, sResponse, UID, "SYSTEM OPERATION", true);
   }




//   protected void responseTime(String sResponse, UserSession4 session4){
//      responseTime(null, sResponse, getUID(session4), getSID(session4), true);
//   }





//   private long getUID(UserSession4 session4){
//      if(session4 == null || session4.profile == null){
//         return -1;
//      }
//      else{
//         return session4.profile.UID;
//      }
//   }
//   private long getUID(UserSession3 session3){
//      if(session3 == null || session3.profile == null){
//         return -1;
//      }
//      else{
//         return session3.profile.UID;
//      }
//   }
//
//
//
//
//   private String getSID(UserSession4 session4){
//      if(session4 == null){
//         return null;
//      }
//      else{
//         return session4.SID;
//      }
//   }
//   private String getSID(UserSession3 session3){
//      if(session3 == null){
//         return null;
//      }
//      else{
//         return session3.SID;
//      }
//   }




   protected void responseTime(String sPOST, String sResponse, Long UID, String SID,
                               boolean bCompress){
      MLogHeader mLogHeader = new MLogHeader();
      mLogHeader.IP = srm.remoteIP;
      mLogHeader.date = System.currentTimeMillis();
      mLogHeader.performance = mLogHeader.date - lStartTime;
      mLogHeader.name = this.getClass().getSimpleName();
      mLogHeader.request_url = srm.URL;
      mLogHeader.request_post = sPOST;
      mLogHeader.UID = UID;
      mLogHeader.SID = SID;
      mLogHeader = MongoLogService.addHeader(mLogHeader);

      if(bCompress) {
         try{
            MLogBody mLogBody = new MLogBody();
            mLogBody.header = mLogHeader._id;

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
            ObjectOutputStream objectOut = new ObjectOutputStream(gzipOut);
            objectOut.writeObject(sResponse);
            objectOut.close();

            mLogBody.response = baos.toByteArray();
            MongoLogService.addBody(mLogBody);
         }
         catch (Throwable e){
            throw new Error("Can not ZIP the '" + sResponse + "'");
         }
      }


      ClientCounters.addRequestTime(this.getClass(), mLogHeader.performance);

      log.info(LOG + "Request time=" + mLogHeader.performance + "ms");
   }




   
   





   protected static final String getCRC32(byte[] data) {
      Checksum checksum = new CRC32();
      checksum.update(data, 0, data.length);
      long lCRC32 = checksum.getValue();

      return java.lang.Long.toHexString(lCRC32).toUpperCase();
   }







   protected static final String getMD5(byte[] data) throws Exception{
      MessageDigest md = MessageDigest.getInstance("MD5");

      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(data);
      oos.flush();
      oos.close();
      bos.close();
      byte[] arrbyteData = bos.toByteArray();

      md.update(arrbyteData);
      byte[] mdbytes = md.digest();

      StringBuilder sb = new StringBuilder();
      for(int i = 0; i < mdbytes.length; i++){
         sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
      }

      return sb.toString();
   }






//   protected JMMIME mimeTypeDetector(String sFileName){
//      JMMIME jmMIME = new JMMIME();
//
//      if(sFileName.endsWith(".jpg")){
//         jmMIME.code = 3;
//         jmMIME.name = "image/jpeg";
//      }
//      else if(sFileName.endsWith(".gif")){
//         jmMIME.code = 10;
//         jmMIME.name = "image/gif";
//      }
//      else if(sFileName.endsWith(".png")){
//         jmMIME.code = 115;
//         jmMIME.name = "image/png";
//      }
//
//      return jmMIME;
//   }








   protected String uglyPath(List<PathSegment> uglyPath){
      StringBuilder sbUglyPath = new StringBuilder();

      for(PathSegment pathSegment : uglyPath){
         if(sbUglyPath.length() > 0) {
            sbUglyPath.append("/");
         }
         sbUglyPath.append(pathSegment.getPath());
      }

      if(sbUglyPath.charAt(0) == '/' && sbUglyPath.charAt(1) == '/'){
         sbUglyPath.replace(0, 2, "/");
      }

      return sbUglyPath.toString();
   }
   
   
   
   protected List<String> uglyPath2Array(List<PathSegment> uglyPath){
      List<String> listResult = new ArrayList<String>();

      for(PathSegment pathSegment : uglyPath){
         listResult.add(pathSegment.getPath());
      }

      return listResult;
   }
   






   protected List<Long> string2LongArray(String sLongArray){

      List<Long> listResult = new ArrayList<Long>();

      String[] arrTSes = sLongArray.split(",");
      for(String sTS : arrTSes){
         listResult.add(Long.parseLong(sTS));
      }

      return listResult;
   }
   

   
   protected void checkSession(String SID) throws AbstractSecurityException{
      try{
         this.UID =  Sessions.getUID(sid2Long(SID));
      }
      catch (Exception e){
         throw new SessionNotFoundException(SID);
      }
   }
   
   
   protected InterfaceConstants.LANGS checkLanguage(String sLang) throws AbstractException{
      try{
         return InterfaceConstants.LANGS.valueOf(sLang);
      }
      catch (Throwable e){
         throw new LangNotSupportedException(sLang);
      }
   }
   
   
   
   protected String long2sid(long SID){
      return Long.toHexString(SID);
   }
   
   
   
   
   protected long sid2Long(String SID){
      return Long.parseLong(SID, 16);      
   }
   
   
   
   
   
   
   
   
   
   
   protected InterfaceConstants.LANGS validateLang(String sLang) throws AbstractException{
      try{
         return InterfaceConstants.LANGS.valueOf(sLang);
      }
      catch (IllegalArgumentException e){
         StringBuffer sbTmp = new StringBuffer();
         
         for(InterfaceConstants.LANGS tmp : InterfaceConstants.LANGS.values()){
            if(sbTmp.length() > 0){
               sbTmp.append(", ");
            }
            
            sbTmp.append(tmp);
         }
         
         throw new BadDataException("LANG", "sLang", sbTmp.toString());
      }
   }
   
   
   
   
   

   
   
   protected void updateStatistic(String sField){
      String sIP = srm.remoteIP;
      Query<MStatisticIP> q =
                    MongoService.ds.createQuery(MStatisticIP.class).filter(MStatisticIP.F_IP, sIP);
      if(q.countAll() == 0){
         MStatisticIP mStatisticIP = new MStatisticIP();
         mStatisticIP.ID = MongoService.getUniqueID(MStatisticIP.class);
         mStatisticIP.IP = sIP;
         try{
            MongoService.ds.save(mStatisticIP);
         }
         catch (Throwable e){
         }
      }
      UpdateOperations<MStatisticIP> uOps =
                            MongoService.ds.createUpdateOperations(MStatisticIP.class).inc(sField);
      MongoService.ds.findAndModify(q, uOps);
      
      
      long lDate = (System.currentTimeMillis() / 1000 / 60 / 60 / 24) * 24 * 60 * 60 * 1000;
      Query<MStatisticDate> q2 =
            MongoService.ds.createQuery(MStatisticDate.class).filter(MStatisticDate.F_DATE, lDate);
      if(q2.countAll() == 0){
         try{
            MStatisticDate mStatisticDate = new MStatisticDate();
            mStatisticDate.ID = MongoService.getUniqueID(MStatisticDate.class);
            mStatisticDate.date = lDate;
            MongoService.ds.save(mStatisticDate);
         }
         catch (Throwable e){
         }
      }
      UpdateOperations<MStatisticDate> uOps2 =
                          MongoService.ds.createUpdateOperations(MStatisticDate.class).inc(sField);
      MongoService.ds.findAndModify(q2, uOps2);
   }
   
   
   
   
   
   ConcurrentHashMap<Integer, MUser> mapUsers  = new ConcurrentHashMap<Integer, MUser>();
   ConcurrentHashMap<Integer, MCafe> mapCafes  = new ConcurrentHashMap<Integer, MCafe>();
   ConcurrentHashMap<Integer, MCity> mapCities = new ConcurrentHashMap<Integer, MCity>();
   public MUser getUser(int ID){
      MUser mUser = mapUsers.get(ID);
      
      if(mUser == null){
         mUser = MongoService.get(MUser.class, ID);
         mapUsers.put(ID, mUser);
         return mUser;
      }
      else{
         return mUser;
      }
   }
   public MCafe getCafe(int ID){
      MCafe mCafe = mapCafes.get(ID);
      
      if(mCafe == null){
         mCafe = MongoService.get(MCafe.class, ID);
         mapCafes.put(ID, mCafe);
         return mCafe;
      }
      else{
         return mCafe;
      }
   }
   public MCity getCity(int ID){
      MCity mCity = mapCities.get(ID);
      
      if(mCity == null){
         mCity = MongoService.get(MCity.class, ID);
         mapCities.put(ID, mCity);
         return mCity;
      }
      else{
         return mCity;
      }
   }
}
