package org.jibble.socnet;

import java.util.*;

public class CommandHandler {

   private SocialNetworkBot _bot;
   private Map _commandMap;
   private Map _contextMap;

   public CommandHandler( SocialNetworkBot bot ) {
      _bot = bot;
      _contextMap = new HashMap();
      _commandMap = new HashMap();
      loadCommands();
   }

   private void loadCommands() {
      AbstractCommand command = null;

      command = new BotSnack();     _commandMap.put(command.getName(), command);
      command = new HelpCommand();  _commandMap.put(command.getName(), command);
      command = new TellCommand();  _commandMap.put(command.getName(), command);
      command = new RmbrCommand();  _commandMap.put(command.getName(), command);
      command = new LearnCommand(); _commandMap.put(command.getName(), command);
      command = new FrgtCommand();  _commandMap.put(command.getName(), command);
      command = new FactCommand();  _commandMap.put(command.getName(), command);
      command = new IndexCommand(); _commandMap.put(command.getName(), command);
   }

   private Map getContext( String channel ) {
      Map context = (Map)_contextMap.get(channel);
      if ( context == null ) {
         context = new HashMap();
         context.put( "command.handler", this );
         context.put( "channel.name", channel );
         _contextMap.put( channel, context );
      }
      return context;
   }

   public Iterator commandNameIterator() {
      return _commandMap.keySet().iterator();
   }

   public void perform( String message, String channel, String sender ) {
      int cmdIndex = message.indexOf( " " );
      if ( cmdIndex == -1 ) cmdIndex = message.length();
      String cmdStr = message.substring( 0, cmdIndex ).trim();
      AbstractCommand command = (AbstractCommand)_commandMap.get( cmdStr );
      if ( command == null ) return; // invoke help here

      command.exec( _bot, getContext( channel ), message );
   }


/////////////////////////////////////////////////////////////////////////////

   private static abstract class AbstractCommand {
      private String _name;
      public AbstractCommand( String name ) { _name = name; }
      public String getName() { return _name; }
      public abstract void exec(SocialNetworkBot bot, Map context, String msg);
   }


   private static class BotSnack extends AbstractCommand {
      private static final int DIGEST_TIME = 60000; // 1 min in ms
      public BotSnack() { super( "botsnack" ); }

      public void exec( SocialNetworkBot bot, Map context, String msg ) {
         String message = "My programmer is an idiot.";
         long now = System.currentTimeMillis();
         Long temp = (Long)context.get( "botsnack.hungry" );
         long hungry = temp == null ? 0 : temp.longValue();
         if ( now > hungry ) { // we're hungry
            hungry = now + DIGEST_TIME;
            message = "Mmmmmmm.  Nummy!";
         } else {
            long delta = hungry - now;
            if ( delta > 5 * DIGEST_TIME ) {
              hungry += 100;
              message = "I'm too full!";
            } else {
              hungry += DIGEST_TIME;
              message = "Mmmmmm.";
            }
         }

         bot.sendMessage( (String)context.get("channel.name"), message );

         context.put( "botsnack.hungry", new Long( hungry ) );
      }
   }//BotSnack

   private static class HelpCommand extends AbstractCommand {
      public HelpCommand() { super("help"); }
      public void exec( SocialNetworkBot bot, Map context, String msg ) {
         String channel = (String)context.get("channel.name");
         CommandHandler hl = (CommandHandler)context.get("command.handler");
         long delay = bot.getMessageDelay();
         bot.setMessageDelay( 100 );
         bot.sendMessage( channel, "I know about the following commands:" );
         Iterator i = hl.commandNameIterator();
         while ( i.hasNext() ) {
            bot.sendMessage( channel, "   "+i.next() );
         }
         bot.setMessageDelay( delay );
      }
   }//HelpCommand

   // Alias "learn"
   private static class RmbrCommand extends LearnCommand {
      public RmbrCommand() { super("remember"); }
   }
   private static class LearnCommand extends AbstractCommand {
      public LearnCommand(String alias) { super(alias); }
      public LearnCommand() { this("learn"); }
      public void exec( SocialNetworkBot bot, Map context, String msg ) {
         String channel = (String)context.get("channel.name");
         Memory memory = (Memory)context.get("channel.memory");
         if ( memory == null ) {
            memory = new Memory( channel );
            context.put("channel.memory", memory );
         }
         int keywordIndex = msg.indexOf(" ");
         if ( keywordIndex == -1 ) {
            // got problem
            return;
         }
         int factoidIndex = msg.indexOf( " ", keywordIndex + 1 );
         if ( factoidIndex == -1 ) {
            // got problem
            return;
         }
         String keyword = msg.substring( keywordIndex, factoidIndex ).trim();
         String factoid = msg.substring( factoidIndex ).trim();
 
         String acknowledgement = memory.remember( keyword, factoid );

         bot.sendMessage( channel, acknowledgement );
      }
   }// LearnCommand

   private static class FrgtCommand extends AbstractCommand {
      public FrgtCommand() { super("forget"); }
      public void exec( SocialNetworkBot bot, Map context, String msg ) {
         String channel = (String)context.get("channel.name");
         Memory memory = (Memory)context.get("channel.memory");
         if ( memory == null ) {
            memory = new Memory( channel );
            context.put("channel.memory", memory );
         }
         int keywordIndex = msg.indexOf(" ");
         if ( keywordIndex == -1 ) {
            // got problem
            return;
         }
         String keyword = msg.substring( keywordIndex ).trim();

         bot.sendMessage( channel, memory.forget( keyword ));
      }
   }

   private static class TellCommand extends AbstractCommand {
      public TellCommand() { super("tell"); }
      public void exec( SocialNetworkBot bot, Map context, String msg ) {
         String channel = (String)context.get("channel.name");
         Memory memory = (Memory)context.get("channel.memory");
         if ( memory == null ) {
            memory = new Memory( channel );
            context.put("channel.memory", memory );
         }
         // by here, I have memory
         // message should have been of the form
         //   tell X [about] Y 
         //   tell X Y
         StringTokenizer tokenizer = new StringTokenizer( msg );
         if ( ! tokenizer.hasMoreTokens() ) { usage( bot,channel ); return;}
         String cmdOrNick = tokenizer.nextToken();
         if ( cmdOrNick.toLowerCase().equals( getName() ) ) {
System.err.println("Did not prune command, I see!");
            if ( ! tokenizer.hasMoreTokens() ) { usage( bot,channel ); return;}
            cmdOrNick = tokenizer.nextToken(); // the nick to tell
         }
         if ( ! tokenizer.hasMoreTokens() ) { usage( bot, channel ); return;}
         String aboutOrKeyword = tokenizer.nextToken();
         if ( aboutOrKeyword.toLowerCase().equals("about") ) {
            if ( ! tokenizer.hasMoreTokens() ) { usage( bot,channel ); return; }
            aboutOrKeyword = tokenizer.nextToken();
         }
System.err.println("asking about '"+aboutOrKeyword+"'");
         String factoid = memory.ask( aboutOrKeyword );

         if ( cmdOrNick.toLowerCase().equals("us") ) {
            cmdOrNick = "ya'll, ";
         } else if ( cmdOrNick.toLowerCase().equals("me") ) {
            cmdOrNick = "okay, ";
         } else {
            cmdOrNick += ": ";
         }
         bot.sendMessage( channel, cmdOrNick + factoid );
      }
      public void usage( SocialNetworkBot bot, String channel ) {
         bot.sendMessage(
            channel, "I don't understand. Try 'tell <nick> about <subject>'."
         );
      }
   }//TellCommand

   private static class FactCommand extends AbstractCommand {
      public FactCommand() { super("facts"); }
      public void exec( SocialNetworkBot bot, Map context, String msg ) {
         String channel = (String)context.get("channel.name");
         Memory memory = (Memory)context.get("channel.memory");
         if ( memory == null ) {
            memory = new Memory( channel );
            context.put("channel.memory", memory );
         }
         bot.sendMessage( channel, "I know "+memory.size()+" facts." );
      }
   }//FactCommand

   private static class IndexCommand extends AbstractCommand {
      public IndexCommand() { super("index"); }
      public void exec( SocialNetworkBot bot, Map context, String msg ) {
         String channel = (String)context.get("channel.name");
         Memory memory = (Memory)context.get("channel.memory");
         if ( memory == null ) {
            memory = new Memory( channel );
            context.put("channel.memory", memory );
         }
         Iterator iterator = memory.keys();
         StringBuffer buffer = new StringBuffer();
         while ( iterator.hasNext() ) {
            buffer.append( iterator.next() ).append(" ");
         } 
         bot.sendMessage( channel, buffer.toString() );
         // A good idea would be to dump the database via a dcc to
         // the asker, or to /msg the index list to the asker
      }
   }//IndexCommand

}
