/*
Copyright (C) 2005 David Green <green@couchpotato.net>
All Rights Reserved.

This file is part of Aelfengard.

Aelfengard is proprietary software. You may not redistribute it without
prior written permission from the copyright holder.
*/

package server.command;

import java.util.HashSet;
import java.util.Set;

import server.Emote;
import server.FatalError;
import server.Player;
import server.RoomEntity;
import server.SyntaxException;

public class CommandProcessor {

    private static int idx;
    private static String cmd;
    
    private static final Set<String> prepositions = new HashSet<String>();
    
    static {
        prepositions.add("to");
        prepositions.add("at");
        prepositions.add("with");
    }

    public static void process(Player player, Command command, String args) {
        idx = 0;
        CommandProcessor.cmd = args;
        try {
            command.run(player);
        } catch (SyntaxException e) {
            throw new FatalError("Invalid Syntax.", e);
        }
    }

    public static void process(Player player, String newCommand) {
        idx = 0;
        CommandProcessor.cmd = newCommand;
        String token = nextToken();
        if (token != null) {
            token = token.toLowerCase();
            try {
                Command command = player.parseCommand(token);
                if (command == null) {
                    // see if player has emotes access (i.e. not dead)
                    Emote spec;
                    if (player.canEmote() && (spec = Emote.get(token)) != null) {
                        doEmote(player, token, spec);
                    }
                    else if (player.isDead()) {
                        player.sendText(true, "You seem to be dead. You can use the @10FLIFE@ZZZ command to return to life.");
                    }
                    else if (player.isUnconscious()) {
                        player.sendText(true, "You're unconscious, but should regain consciousness soon.");
                    }
                    else {
                        player.sendText(true, "What?");
                    }
                }
                else {
                    command.run(player);
                }
            }
            catch (SyntaxException ex) {
                player.sendText(true, "Invalid syntax. Type @10FHELP " + token.toUpperCase() + "@ZZZ for input patterns.");
            }
        }
    }
    
    private static void doEmote(Player player, String name, Emote spec) throws SyntaxException {
        String modifier = null;
        String target = null;
        while (true) {
            String token = nextToken();
            if (token == null) {
                break;
            }
            if (spec.getTargetType() != Emote.TargetType.DISALLOWED) {
                if (prepositions.contains(token)) {
                    continue; // skip preposition
                }
            }
            String tmp = spec.getModifier(token);
            if (tmp != null) {
                if (modifier != null) {
                    throw new SyntaxException();
                }
                modifier = tmp;
                continue;
            }
            if (spec.getTargetType() != Emote.TargetType.DISALLOWED) {
                if (target != null) {
                    throw new SyntaxException();
                }
                target = token;
            }
            else {
                throw new SyntaxException();
            }
        }
        if (modifier == null) {
            modifier = "";
        }
        else {
            modifier = " " + modifier;
        }
        if (target != null) {
            RoomEntity entity = player.matchLocalRoomEntity(target);
            if (entity == null) {
                player.sendText(true, "There's noone here by that name.");
            }
            else {
                player.getRoom().sendText(null, spec.getAudioTS(), spec.getVisualTS(), spec.getBothTS(), null, new String[] { modifier }, player, entity);
            }
        }
        else {
            if (spec.getTargetType() == Emote.TargetType.REQUIRED) {
                player.sendText(true, "A target is required for this emote.");
            }
            else {
                player.getRoom().sendText(null, spec.getAudioTS(), spec.getVisualTS(), spec.getBothTS(), null, new String[] { modifier }, player);
            }
        }
    }

    public static void help(Player player, String token) {
        token = token.toLowerCase();
        Command command = player.parseCommand(token);
        if (command == null) {
            // Maybe it's an emote
            Emote spec;
            if (!player.canEmote() || (spec = Emote.get(token)) == null) {
                player.sendText(true, "Unknown help topic: " + token);
                return;
            }
            token = token.toUpperCase();
            StringBuffer syntax = new StringBuffer();
            syntax.append("@10F@507Syntax@107:@ZZZ ");
            syntax.append(token);
            Set<String> modifiers = spec.getModifiers();
            if (!modifiers.isEmpty()) {
                syntax.append(" [modifier]");
            }
            switch (spec.getTargetType()) {
                case REQUIRED:   syntax.append(" <target>"); break;
                case ALLOWED:    syntax.append(" [target]"); break;
                case DISALLOWED: break;
            }
            player.sendText(true, syntax.toString());
            if (!modifiers.isEmpty()) {
                syntax = new StringBuffer();
                for (String modifier : modifiers) {
                    if (syntax.length() > 0) {
                        syntax.append(", ");
                    }
                    syntax.append(modifier);
                }
                player.sendText(true, "@10F@207Valid modifiers:@ZZZ " + syntax.toString());
            }
            return;
        }
        token = token.toUpperCase();
        String[] helpText = command.getHelpText();
        if (helpText != null && helpText.length > 0) {
            player.sendText(false, "");
            for (int i = 0; i < helpText.length; i++) {
                player.sendText(false, helpText[i]);
            }
        }
        String[] syntax = command.getSyntax(player);
        player.sendText(true, "@10F@507Syntax@107:@ZZZ " + token + " " + syntax[0]);
        for (int i = 1; i < syntax.length; i++) {
            player.sendText(false, "@10F        " + token + " " + syntax[i]);
        }
    }

    static String nextToken() {
        String token = nextTokenPreserveCase();
        return token == null ? null : token.toLowerCase();
    }
    
    static String nextTokenPreserveCase() {
        while (true) {
            if (idx >= cmd.length()) {
                return null; // no more tokens
            }
            int end = cmd.indexOf(' ', idx);
            if (end < 0) {
                end = cmd.length();
            }
            if (end > idx) {
                String token = cmd.substring(idx, end);
                idx = end + 1; // skip space for next time
                return token.trim();
            }
            // skip this space and go to the next one
            idx++;
        }
    }
    
    static String nextTokenAllowQuotes() {
        return nextTokenAllowQuotes(false);
    }
    
    static String getRemainingAllowQuotes() {
        return nextTokenAllowQuotes(true);
    }
    
    private static String nextTokenAllowQuotes(boolean restOfLine) {
        char[] c = cmd.toCharArray();
        // skip any initial spaces
        while (true) {
            if (idx >= c.length) {
                return null; // no more tokens
            }
            if (c[idx] != ' ') {
                break;
            }
            idx++;
        }
        // start to compile the token text
        StringBuffer sb = new StringBuffer();
        while (true) {
            if (idx >= c.length) {
                return sb.toString();
            }
            switch (c[idx]) {
                case '"': {
                    int end = cmd.indexOf('"', idx + 1);
                    if (end < 0) {
                        end = cmd.length();
                    }
                    sb.append(c, idx + 1, end - idx - 1);
                    idx = end;
                    break;
                }
                case ' ': {
                    if (!restOfLine) {
                        // all done
                        idx++; // skip the space for next time
                        return sb.toString();
                    }
                }
                default: sb.append(c[idx]);
            }
            idx++;
        }
    }

    static String getRemaining() {
        String s = "";
        if (idx < cmd.length()) {
            s = cmd.substring(idx);
        }
        idx = cmd.length();
        return s.trim();
    }
    

}