package net.sf.l2j.gameserver.model.entity;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javolution.util.FastComparator;
import javolution.util.FastMap;
import javolution.util.FastTable;
import net.sf.l2j.Config;
import net.sf.l2j.L2DatabaseFactory;
import net.sf.l2j.gameserver.ThreadPoolManager;
import net.sf.l2j.gameserver.datatables.ItemTable;
import net.sf.l2j.gameserver.idfactory.IdFactory;
import net.sf.l2j.gameserver.model.L2ItemInstance;
import net.sf.l2j.gameserver.model.L2World;
import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
import net.sf.l2j.gameserver.network.SystemMessageId;
import net.sf.l2j.gameserver.network.serverpackets.InventoryUpdate;
import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;

public class L2TopManager
{
	
  protected static final Logger _log = Logger.getLogger(L2TopManager.class.getName());
  private Pattern _webPattern;
  private Pattern _smsPattern;
  private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

  private static L2TopManager _instance = null;
  private static FastMap<Integer, Long> _voteDateCache = new FastMap<Integer, Long>();

  private static String USERAGENT = "Mozilla/5.0 (%Platform%; %Version%; %Architecture%; U) %JVM%/%JVMVer%".replace("%Platform%", System.getProperty("os.name")).replace("%Version%", System.getProperty("os.version")).replace("%Architecture%", System.getProperty("os.arch")).replace("%JVM%", System.getProperty("java.vm.name")).replace("%JVMVer%", System.getProperty("java.vm.version"));

  public static L2TopManager getInstance()
  {
    if (_instance == null)
      _instance = new L2TopManager();
    return _instance;
  }

  private L2TopManager()
  {
    if (Config.L2TOPRU_DELAY < 1L)
      return;
    _log.info("L2TopManager: Initializing.");
    this._webPattern = Pattern.compile("^([\\d-]+\\s[\\d:]+)\\s+(?:" + Config.L2TOPRU_PREFIX + "-)*([\\w\\W]+?)$", 8);
    this._smsPattern = Pattern.compile("^([\\d-]+\\s[\\d:]+)\\s+(?:" + Config.L2TOPRU_PREFIX + "-)*([\\w\\W]+?)\\s+x(\\d{1,2})$", 8);

    ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new L2TopRuTask(), Config.L2TOPRU_DELAY, Config.L2TOPRU_DELAY);
  }

  private FastTable<L2TopRuVote> filterVotes(FastTable<L2TopRuVote> votes)
  {
    FastTable result = new FastTable();
    FastMap chars = new FastMap();
    Connection con = null;
    Statement stmt = null;
    ResultSet rset = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      stmt = con.createStatement();
      rset = stmt.executeQuery("SELECT `obj_Id`,`char_name` FROM `characters`");

      while (rset.next()) {
        chars.put(rset.getString("char_name").toLowerCase(), Integer.valueOf(rset.getInt("obj_Id")));
      }
      stmt.close();
      rset.close();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    finally
    {
      if (con != null) try { con.close();
        } catch (Exception ex) {
        } 
    }
    int charObjId = 0;
    for (L2TopRuVote vote : votes)
    {
      if (chars.containsKey(vote.charname))
      {
        charObjId = ((Integer)chars.get(vote.charname)).intValue();
        if (isRewardReq(charObjId, vote.datetime))
        {
          vote.char_obj_id = charObjId;
          result.add(vote);
        }
      }
    }
    return result;
  }

  private boolean isRewardReq(int charObjId, long date)
  {
    long lastDate = 0L;
    Connection con = null;
    PreparedStatement pstmt = null;
    ResultSet rset = null;

    if (_voteDateCache.containsKey(Integer.valueOf(charObjId)))
    {
      lastDate = ((Long)_voteDateCache.get(Integer.valueOf(charObjId))).longValue();
      if (date > lastDate)
      {
        _voteDateCache.put(Integer.valueOf(charObjId), Long.valueOf(date));
        try
        {
          con = L2DatabaseFactory.getInstance().getConnection();
          pstmt = con.prepareStatement("REPLACE DELAYED INTO `l2top_votes`(`obj_Id`,`last_vote`) VALUES (?,?)");
          pstmt.setInt(1, charObjId);
          pstmt.setLong(2, date);
          pstmt.executeUpdate();
          pstmt.close();
          if (con != null) try { con.close();
            } catch (Exception ex) {
            } 
        }
        catch (Exception ex) {
          ex.printStackTrace();
        }
        return true;
      }
    }
    else
    {
      try
      {
        con = L2DatabaseFactory.getInstance().getConnection();
        pstmt = con.prepareStatement("SELECT `obj_Id`,`last_vote` FROM `l2top_votes` WHERE `obj_Id` = ?");
        pstmt.setInt(1, charObjId);
        rset = pstmt.executeQuery();
        if (rset.next())
        {
          lastDate = rset.getLong("last_vote");
          pstmt.close();
          rset.close();
          if (date > lastDate)
          {
            _voteDateCache.put(Integer.valueOf(charObjId), Long.valueOf(date));
            try
            {
              pstmt = con.prepareStatement("REPLACE DELAYED INTO `l2top_votes`(`obj_Id`,`last_vote`) VALUES (?,?)");
              pstmt.setInt(1, charObjId);
              pstmt.setLong(2, date);
              pstmt.executeUpdate();
              pstmt.close();
              if (con != null) con.close();
            }
            catch (Exception ex)
            {
              ex.printStackTrace();
            }

          }
          _voteDateCache.put(Integer.valueOf(charObjId), Long.valueOf(lastDate));
        }
        else
        {
          pstmt.close();
          rset.close();
          _voteDateCache.put(Integer.valueOf(charObjId), Long.valueOf(date));
          try
          {
            pstmt = con.prepareStatement("REPLACE DELAYED INTO `l2top_votes`(`obj_Id`,`last_vote`) VALUES (?,?)");
            pstmt.setInt(1, charObjId);
            pstmt.setLong(2, date);
            pstmt.executeUpdate();
            pstmt.close();
            if (con != null) try { con.close();
              } catch (Exception ex) {
              } 
          }
          catch (Exception ex) {
            ex.printStackTrace();
          }


        }
      }
      catch (Exception ex)
      {
        ex.printStackTrace();
      }
      finally
      {
        if (con != null) try { con.close(); } catch (Exception ex) {
          } 
      }
    }
    return false;
  }

  private void rewardVotes(FastTable<L2TopRuVote> votes)
  {
    for (L2TopRuVote vote : votes)
    {
      switch (vote.type.ordinal())
      {
      case 1:
        _log.info("L2TopManager: Rewarding " + vote.toString());
        giveItem(vote.char_obj_id, Config.L2TOPRU_WEB_REWARD_ITEMID, Config.L2TOPRU_WEB_REWARD_ITEMCOUNT);

        break;
      case 2:
        _log.info("L2TopManager: Rewarding " + vote.toString());
        giveItem(vote.char_obj_id, Config.L2TOPRU_SMS_REWARD_ITEMID, Config.L2TOPRU_SMS_REWARD_VOTE_MULTI ? Config.L2TOPRU_SMS_REWARD_ITEMCOUNT * vote.count : Config.L2TOPRU_SMS_REWARD_ITEMCOUNT);
      }
    }
  }

  private void giveItem(int charObjId, int itemId, int itemCount)
  {
    if (charObjId < 1)
      return;
    L2PcInstance player = (L2PcInstance)L2World.getInstance().findObject(charObjId);
    if (player != null)
    {
      L2ItemInstance item = player.getInventory().addItem("L2TopVoting", itemId, itemCount, null, null);
      InventoryUpdate iu = new InventoryUpdate();
      iu.addItem(item);
      player.sendPacket(iu);
      player.sendMessage(Config.L2TOP_MESSAGE);
      SystemMessage sm = new SystemMessage(SystemMessageId.YOU_PICKED_UP_S1_S2);
      sm.addItemName(itemId);
      sm.addNumber(itemCount);
      player.sendPacket(sm);
    }
    else
    {
      L2ItemInstance newItem = new L2ItemInstance(IdFactory.getInstance().getNextId(), ItemTable.getInstance().getTemplate(itemId));
      newItem.setCount(itemCount);
      newItem.setOwnerId(charObjId);
      newItem.setLocation(L2ItemInstance.ItemLocation.INVENTORY);
      newItem.updateDatabase();
    }
  }

  private FastTable<L2TopRuVote> getAllVotes()
  {
    FastTable result = new FastTable();
    try
    {
      Matcher m = this._webPattern.matcher(getPage(Config.L2TOPRU_WEB_VOTE_URL));
      while (m.find())
      {
        L2TopRuVote vote = new L2TopRuVote(m.group(1), m.group(2));
        result.add(vote);
      }

      m = this._smsPattern.matcher(getPage(Config.L2TOPRU_SMS_VOTE_URL));
      while (m.find())
      {
        L2TopRuVote vote = new L2TopRuVote(m.group(1), m.group(2), m.group(3));
        result.add(vote);
      }

      result.setValueComparator(new L2TopRuVoteComparator());
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    result.sort();
    return result;
  }

  private void tick()
  {
    _log.info("L2TopManager: Rewarding votes.");
    rewardVotes(filterVotes(getAllVotes()));
  }

  private String getPage(String uri) throws Exception
  {
    URL url = new URL(uri);
    URLConnection conn = url.openConnection();

    conn.addRequestProperty("Host", url.getHost());
    conn.addRequestProperty("Accept", "*/*");
    conn.addRequestProperty("Connection", "close");
    conn.addRequestProperty("User-Agent", USERAGENT);
    conn.setConnectTimeout(30000);

    BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "cp1251"));
    StringBuilder builder = new StringBuilder();
    String line = null;
    while ((line = in.readLine()) != null)
    {
      builder.append(line).append("\n");
    }

    return builder.toString();
  }

  private final class L2TopRuVoteComparator<T> extends FastComparator<L2TopManager.L2TopRuVote>
  {
    private L2TopRuVoteComparator()
    {
    }

    public int hashCodeOf(L2TopManager.L2TopRuVote o)
    {
      return o.hashCode();
    }

    public boolean areEqual(L2TopManager.L2TopRuVote o, L2TopManager.L2TopRuVote o1)
    {
      return (o.char_obj_id == o1.char_obj_id) && (o.datetime == o1.datetime);
    }

    public int compare(L2TopManager.L2TopRuVote o, L2TopManager.L2TopRuVote o1)
    {
      if (o.datetime == o1.datetime)
        return 0;
      if (o.datetime < o1.datetime)
        return -2147483648;
      if (o.datetime > o1.datetime)
        return 2147483647;
      return -1;
    }
  }

  private class L2TopRuVote
  {
    public long datetime;
    public String charname;
    public int count;
    public int char_obj_id = -1;
    public L2TopManager.L2TopRuVoteType type;

    public L2TopRuVote(String date, String charName, String itemcount)
      throws Exception
    {
      this.datetime = (L2TopManager.DATE_FORMAT.parse(date).getTime() / 1000L);
      this.count = Byte.parseByte(itemcount);
      this.charname = charName.toLowerCase();
      this.type = L2TopManager.L2TopRuVoteType.SMS;
    }

    public L2TopRuVote(String date, String charName) throws Exception
    {
      this.datetime = (L2TopManager.DATE_FORMAT.parse(date).getTime() / 1000L);
      this.charname = charName.toLowerCase();
      this.count = 1;
      this.type = L2TopManager.L2TopRuVoteType.WEB;
    }

    public String toString()
    {
      return this.charname + "-" + this.count + "[" + this.char_obj_id + "(" + this.datetime + "|" + this.type.name() + ")]";
    }
  }

  private static enum L2TopRuVoteType
  {
    WEB, 
    SMS;
  }

  private class L2TopRuTask
    implements Runnable
  {
    private L2TopRuTask()
    {
    }

    public void run()
    {
      L2TopManager.getInstance().tick();
    }
  }
}