package com.cardshifter.gdx.ui.cards;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.NinePatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup;
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable;
import com.cardshifter.api.outgoing.CardInfoMessage;
import com.cardshifter.api.outgoing.UsableActionMessage;
import com.cardshifter.gdx.CardshifterGame;
import com.cardshifter.gdx.TargetStatus;
import com.cardshifter.gdx.TargetableCallback;
import com.cardshifter.gdx.ZoomCardCallback;
import com.badlogic.gdx.scenes.scene2d.utils.DragListener;
import com.cardshifter.gdx.screens.DeckBuilderScreen;
import com.cardshifter.gdx.screens.GameScreen;
import com.cardshifter.gdx.ui.CardshifterClientContext;
import com.cardshifter.gdx.ui.res.ResourceView;
import com.cardshifter.gdx.ui.res.ResViewFactory;

import java.util.*;

public class CardViewSmall extends DefaultCardView {

    private final Table table;
    //private final Label effect;
    private final Label namedEffect;
    private final Label complexEffect;
    private final Label name;
    private final ResourceView cost;
    private final ResourceView stats;
    private final Map<String, Object> properties;
    private final int id;
    private final CardshifterClientContext context;
    private TargetableCallback callback;
    private ZoomCardCallback zoomCallback;
    private final NinePatch patch = new NinePatch(new Texture(Gdx.files.internal("cardbg.png")), 3, 3, 3, 3);
    private final List<UsableActionMessage> actions = new ArrayList<UsableActionMessage>(5);
    public boolean isZoomed = false;
    public final CardInfoMessage cardInfo;
    private final float screenWidth;
    private final float screenHeight;
    private ClickListener clickListener;
    private ActorGestureListener longPressListener;
    private DragListener dragListener;
    
    public class MyDragListener extends DragListener {
    	private Table table;
    	private float startX = 0;
    	private float startY = 0;
    	public MyDragListener(Table table) {
    		this.table = table;
    	}
        public void drag(InputEvent event, float x, float y, int pointer) {
            table.moveBy(x - table.getWidth() / 2, y - table.getHeight() / 2);
        }
    	public void dragStart (InputEvent event, float x, float y, int pointer) {
    		if (this.startX == 0 && this.startY == 0) {
        		this.startX = this.table.getX();
        		this.startY = this.table.getY();
    		}
    	}
    	public void dragStop (InputEvent event, float x, float y, int pointer) {
    		if (CardViewSmall.this.callback != null) {
    			if (CardViewSmall.this.callback instanceof DeckBuilderScreen) {
        			if (!((DeckBuilderScreen)callback).checkCardDrop(CardViewSmall.this)) {
        	    		table.addAction(Actions.moveTo(this.startX, this.startY, 0.2f));
        			}
    			} else if (CardViewSmall.this.callback instanceof GameScreen) {
    				if (!((GameScreen)callback).checkCardDrop(CardViewSmall.this)) {
    					table.addAction(Actions.moveTo(this.startX, this.startY, 0.2f));
    				} else {
    					CardViewSmall.this.performAction();
    				}
    			}
    		} else {
    			table.addAction(Actions.moveTo(this.startX, this.startY, 0.2f));
    		}
    	}
    }
    
    private void performAction() {
        UsableActionMessage action = actions.get(0);
        context.sendAction(action);
    }

    public CardViewSmall(CardshifterClientContext context, CardInfoMessage cardInfo, ZoomCardCallback zoomCallback, boolean zoomedVersion) {
        this.context = context;
        this.cardInfo = cardInfo;
        this.properties = new HashMap<String, Object>(cardInfo.getProperties());
        this.id = cardInfo.getId();
        this.zoomCallback = zoomCallback;
        this.screenWidth = CardshifterGame.STAGE_WIDTH;
        this.screenHeight = CardshifterGame.STAGE_HEIGHT;

        table = new Table(context.getSkin());
        table.setBackground(new NinePatchDrawable(patch));
        table.defaults().height(this.screenHeight/30);
        Gdx.app.log("CardView", "Creating for " + cardInfo.getProperties());
        table.defaults().expand();
        name = label(context, cardInfo, "name");
        table.add(name).colspan(2).width(this.screenWidth/8).left().row();
        // table.add(image);
        //effect = label(context, cardInfo, "effect");
        this.namedEffect = new Label(" ", context.getSkin());
        String resourceName = this.stringResources(cardInfo);
        if (resourceName != null && !resourceName.equals("")) {
        	this.namedEffect.setText(this.stringResources(cardInfo));
        }
        //effect.setText(effect.getText() + stringResources(cardInfo));
        table.add(namedEffect).colspan(2).width(this.screenWidth/8).left().row();
        this.complexEffect = label(context, cardInfo, "effect");
        table.add(complexEffect).colspan(2).width(this.screenWidth/8).center().row();
        
        if (zoomedVersion) {
        	Label flavorLabel = label(context, cardInfo, "flavor");
        	flavorLabel.setWrap(true);
        	table.add(flavorLabel).colspan(2).width(this.screenWidth/8).center().row();
        }
        
        ResViewFactory rvf = new ResViewFactory(context.getSkin());
        cost = rvf.forFormat(rvf.res("MANA_COST"), rvf.res("SCRAP_COST"));
        table.add(cost.getActor()).colspan(2).right().row();

        stats = rvf.forFormat(rvf.coloredRes("ATTACK", properties), rvf.str("/"), rvf.coloredRes("HEALTH", "MAX_HEALTH"));
        table.add(label(context, cardInfo, "creatureType")).left();
        table.add(stats.getActor()).right();

        cost.update(properties);
        stats.update(properties);
        
        this.clickListener = new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
            	if (CardViewSmall.this.dragListener.isDragging()) {
            		return;
            	}
            	if (this.getTapCount() > 1) {
            		CardViewSmall.this.zoomed();
            	} else {
            		CardViewSmall.this.clicked();
            	} 
            }
        };
        this.clickListener.setTapCountInterval(0.2f); //i think that the default is 0.4f
        this.longPressListener = new ActorGestureListener(){
            @Override
            public boolean longPress(Actor actor, float x, float y) {
            	CardViewSmall.this.zoomed();
                return true;
            }
        };
        this.longPressListener.getGestureDetector().setLongPressSeconds(0.3f);
        this.dragListener = new MyDragListener(this.table);
    	
        table.addListener(this.clickListener);
        table.addListener(this.longPressListener);
        table.addListener(this.dragListener);

        table.setTouchable(Touchable.enabled);
    }

    private String stringResources(CardInfoMessage cardInfo) {
        StringBuilder str = new StringBuilder();
        Map<String, Object> props = cardInfo.getProperties();
        if (Integer.valueOf(0).equals(props.get("SICKNESS"))) {
            str.append(" Rush");
        }
        if (Integer.valueOf(1).equals(props.get("DENY_COUNTERATTACK"))) {
            str.append(" Ranged");
        }
        return str.toString();
    }

    private void zoomed() {
    	if (this.zoomCallback != null) {
    		this.zoomCallback.zoomCard(this);
    	}
    }

    private void clicked() {
        Gdx.app.log("CardView", "clicked on " + id);
        if (this.zoomCallback != null) {
        	this.zoomCallback.endZoom(this);
        }
        if (callback != null) {
            callback.addEntity(this);
        } 
        if (!actions.isEmpty()) {
            if (actions.size() == 1) {
                UsableActionMessage action = actions.get(0);
                context.sendAction(action);
            }
            else {
                Dialog dialog = new Dialog("Choose Action", context.getSkin()) {
                    @Override
                    protected void result(Object object) {
                        context.sendAction(actions.get((Integer) object));
                    }
                };
                dialog.text("Which action?");
                ListIterator<UsableActionMessage> it = actions.listIterator();
                while (it.hasNext()) {
                    int i = it.nextIndex();
                    UsableActionMessage action = it.next();
                    dialog.button(action.getAction(), i);
                }
                dialog.show(context.getStage());
            }
        }
    }

    public static Label label(CardshifterClientContext context, CardInfoMessage message, String key) {
        Object value = message.getProperties().get(key);
        //by changing the following to " ", a row will always be added
        //also need to replace newline characters because of some bug on server side
        String labelString = String.valueOf(value == null ? " " : value);
        labelString = labelString.replace("\n", "").replace("\r", "");
        Label label = new Label(labelString, context.getSkin());
        label.setEllipsis(true);
        return label;
    }

    @Override
    public void set(Object key, Object value) {
        if ("HEALTH".equals(key)) {
            Integer health = (Integer) value;
            Integer oldHealth = (Integer) properties.get(key);
            int diff = health - oldHealth;
            WidgetGroup grp = (WidgetGroup) table.getParent();
            grp.layout();
            
            if (diff != 0) {
                Vector2 pos = new Vector2(table.getWidth() / 2, table.getHeight() / 2);
                table.localToStageCoordinates(pos);
                final Label changeLabel = new Label(String.valueOf(diff), context.getSkin());
                Gdx.app.log("Anim", "Create health animation at " + pos.x + ", " + pos.y);
                changeLabel.setPosition(pos.x, pos.y);
                if (diff > 0) {
                	changeLabel.setColor(Color.GREEN);
                } else {
                	changeLabel.setColor(Color.RED);
                }
                changeLabel.addAction(Actions.sequence(Actions.moveBy(0, this.screenHeight/8, 1.5f), Actions.run(new Runnable() {
                    @Override
                    public void run() {
                        changeLabel.remove();
                    }
                })));
                context.getStage().addActor(changeLabel);
            }
        }
        properties.put((String) key, value);
        cost.update(properties);
        stats.update(properties);
    }

    @Override
    public void setTargetable(TargetStatus targetable, TargetableCallback callback) {
        table.setColor(targetable.getColor());
        this.callback = callback;
    }

    @Override
    public int getId() {
        return this.id;
    }

    @Override
    public void usableAction(UsableActionMessage message) {
        table.setColor(1, 1, 1, 1f);
        actions.add(message);
    }

    @Override
    public void clearUsableActions() {
        table.setColor(1, 1, 1, 0.5f);
        actions.clear();
    }

    @Override
    public Map<String, Object> getInfo() {
        return new HashMap<String, Object>(this.properties);
    }

    @Override
    public Actor getActor() {
        return table;
    }
    
    public void zoom() {
    	this.isZoomed = true;
    	this.name.setEllipsis(false);
    	this.name.layout();
    	this.complexEffect.setEllipsis(false);
    	this.complexEffect.setWrap(true);
    	this.complexEffect.layout();
    }
    
    public void endZoom() {
    	this.isZoomed = false;
    	this.name.setEllipsis(true);
    	this.name.layout();
    	this.complexEffect.setEllipsis(true);
    	this.complexEffect.setWrap(false);
    	this.complexEffect.layout();
    }
    
    //workaround for the white border active cards
    public void setUsable(TargetableCallback screen) {
    	this.callback = screen;
    }
}