amfservices.actions.PGServicesAction.java Source code

Java tutorial

Introduction

Here is the source code for amfservices.actions.PGServicesAction.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package amfservices.actions;

import db.PGKeys;
import config.CFCote;
import config.CFMainQuests;
import config.CFPenguin;
import config.CFRandomizePrize;
import config.PGConfig;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import libCore.SNServices;
import org.apache.commons.codec.digest.DigestUtils;
import org.json.simple.JSONValue;
import pgentity.*;
import pgentity.EntityContext;
//import pgentity.events.release_event.ReleaseEventServices;
import pgentity.prize.PGPrize;
import pgentity.prize.PrizeFactory;
import pgentity.quest.*;
import pgentity.services.*;
import share.*;
import share.AMFBuilder;
import share.PGException;
import zme.api.exception.ZingMeApiException;

/**
 *
 * @author KieuAnh
 */
public class PGServicesAction {
    private PGServicesAction() {
        super();
    }

    private static final PGServicesAction inst = new PGServicesAction();

    public static PGServicesAction inst() {
        return inst;
    }

    public Map<String, Object> loadGameAction(String uid, String signedReq, long now)
            throws PGException, IOException, ZingMeApiException {
        User user;
        if (User.isExist(uid)) {
            AutoMigrate.inst().migrate(uid);
            user = User.getUser(uid);
        } else {
            user = UserServices.inst().createNewUser(uid, signedReq, now);
        }

        EntityContext context = EntityContext.getContext(user);

        QuestServices.inst().updateQuest(context, now);

        QuestLogger questLogger = QuestServices.inst().getQuestLogger(uid, now);
        CoteServices.inst().updateCote(context, questLogger, now);

        context.saveToDB();

        UserServices.inst().registerLogin(questLogger, uid, signedReq, now);

        // build response
        Map<String, Object> response = new HashMap();
        response.put(PGMacro.USER, user.buildFullAMF(true, false, true, true, now));

        Map<String, Object> playWithFriend = new HashMap();
        playWithFriend.put(PGMacro.NUMBER_FRIEND_HELPED_FISH,
                HelpFriendFish.getEntity(uid, now).numberFriendHelped());
        playWithFriend.put(PGMacro.NUMBER_FRIEND_STOLEN_EGG,
                StealFriendEgg.getEntity(uid, now).numberFriendStolen());
        response.put(PGMacro.PLAY_WITH_FRIEND, playWithFriend);

        response.put(PGMacro.QUEST, QuestServices.inst().buildQuestAMF(context, now));

        //        response.put(PGMacro.RELEASE_EVENT,
        //                ReleaseEventServices.SERVICES.buildAMF(uid));

        return response;
    }

    public Map<String, Object> getFriendListAction(String uid) throws PGException {
        FriendList friendList = FriendList.getFriendList(uid);
        return friendList.buildAMF();
    }

    public Map<String, Object> penguinWannaEatAction(String uid, String coteID, List<String> penguinIDs, long now)
            throws PGException {
        final EntityContext context = EntityContext.getContext(uid);

        for (String pengId : penguinIDs) {
            PGException.Assert(context.getCote().penguins().contains(pengId), PGError.PENGUIN_NOT_IN_COTE,
                    "Penguin isn't contained in cote");
        }

        PriorityQueue<Penguin> penguins = new PriorityQueue(penguinIDs.size(), new Comparator<Penguin>() {
            @Override
            public int compare(Penguin p1, Penguin p2) {
                long p1NextEatTime = PenguinServices.inst().nextEat(p1, context.getCote());
                long p2NextEatTime = PenguinServices.inst().nextEat(p2, context.getCote());

                return (p1NextEatTime > p2NextEatTime) ? 1 : ((p1NextEatTime == p2NextEatTime) ? 0 : -1);
            }
        });

        Map<String, Object> failData = new HashMap();

        int remainFish = context.getCote().getPoolFish();
        for (String pengId : penguinIDs) {
            Penguin penguin = Penguin.getPenguin(uid, coteID, pengId);

            long nextEat = PenguinServices.inst().nextEat(penguin, context.getCote());
            if (nextEat > now) {
                Map<String, Object> lastPenguinEatData = new HashMap();
                lastPenguinEatData.put(PGMacro.TIME_LAST_EAT, penguin.getLastEat());
                lastPenguinEatData.put(PGMacro.FISH_LAST_EAT, penguin.getFood());

                failData.put(penguin.getPenguinID(), lastPenguinEatData);
            } else {
                PGException.Assert(remainFish > 0, PGError.EMPTY_POOL, "Empty pool");
                PGException.Assert(PenguinServices.inst().configOf(penguin).getFeed() > 0,
                        PGError.PENGUIN_CANNOT_EAT, "Penguin cannot eat");

                penguins.add(penguin);
                remainFish -= Math.min(PenguinServices.inst().configOf(penguin).getFeed(), remainFish);
            }
        }

        List<Penguin> fedPenguins = new ArrayList(penguinIDs.size());
        while (!penguins.isEmpty()) {
            Penguin penguin = penguins.poll();
            long nextEat = PenguinServices.inst().nextEat(penguin, context.getCote());

            QuestLogger questLogger = QuestServices.inst().getQuestLogger(uid, now);
            PenguinServices.inst().eat(penguin, context, questLogger, nextEat);
            fedPenguins.add(penguin);
        }

        Map<String, Object> successData = new HashMap();
        for (Penguin penguin : fedPenguins) {
            penguin.saveToDB();
            successData.put(penguin.getPenguinID(), AMFBuilder.make(PGMacro.FISH_LAST_EAT, penguin.getFood(),
                    PGMacro.TIME_LAST_EAT, penguin.getLastEat()));
        }

        context.saveToDB();

        Map<String, Object> response = new HashMap();
        response.put(PGMacro.SUCCESS, successData);
        response.put(PGMacro.FAIL, failData);

        return response;
    }

    public Map<String, Object> dropFishAction(String uid, String coteID, int nFish, long now) throws PGException {
        EntityContext context = EntityContext.getContext(uid);

        PGException.Assert(context.getUser().getFish() >= nFish, PGError.NOT_ENOUGH_FISH,
                "Not enough fish (" + context.getUser().getFish() + ")");

        QuestLogger questLogger = QuestServices.inst().getQuestLogger(uid, now);

        Map<String, Integer> atePenguins = CoteServices.inst().dropFish(context, questLogger, nFish, now);

        questLogger.log(new DropFishRecord());
        questLogger.log(new DropFish_FishRecord(nFish));

        context.getUser().setFish(context.getUser().getFish() - nFish);
        //PGUserServices.inst().increaseUserExp(context, (int) (nFish * PGConst.FISH_TO_EXP), now);

        context.saveToDB();

        return AMFBuilder.toAMF(atePenguins);
    }

    public Map<String, Object> upgradeCoteAction(String uid, String coteId, long now) throws PGException {
        User user = User.getUser(uid);
        user.updateFromDB();

        Cote cote = Cote.getCote(uid, coteId);
        cote.updateFromDB();
        PGException.Assert(user.cotes().contains(cote.getCoteID()), PGError.NOT_FRIEND_COTE,
                "Cote's not current belong to user");

        QuestLogger userQLogger = QuestServices.inst().getQuestLogger(uid, now);
        CoteServices.inst().upgradeCote(user, userQLogger, cote);

        // update to db
        cote.saveToDB();
        user.saveToDB();

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.SUCCESS, true);
        result.put(PGMacro.LEVEL, cote.getLevel());
        return result;
    }

    public Map<String, Object> upgradeBoxEggAction(String uid, String coteID, long now) throws PGException {
        User user = User.getUser(uid);
        user.updateFromDB();

        Cote cote = Cote.getCote(uid, coteID);
        cote.updateFromDB();
        PGException.Assert(user.cotes().contains(cote.getCoteID()), PGError.NOT_USER_COTE,
                "Cote's not currently belong to user");

        BoxEgg boxegg = BoxEgg.getBoxEgg(uid, coteID);
        QuestLogger userQLogger = QuestServices.inst().getQuestLogger(uid, now);

        BoxEggServices.inst().upgradeBoxEgg(user, userQLogger, boxegg);

        // update to db
        boxegg.saveToDB();
        user.saveToDB();

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.SUCCESS, true);
        result.put(PGMacro.LEVEL, boxegg.getLevel());
        return result;
    }

    public Map<String, Object> sellPenguinAction(String uid, String coteID, String penguinId, long now)
            throws PGException {
        Cote cote = Cote.getCote(uid, coteID);
        PGException.Assert(cote.penguins().contains(penguinId), PGError.PENGUIN_NOT_IN_COTE,
                "Penguin's not contained in cote: " + coteID);

        Penguin penguin = Penguin.getPenguin(uid, coteID, penguinId);
        int pLevel = penguin.getLevel();

        Penguin.destroy(uid, coteID, penguinId);
        cote.penguins().remove(penguinId);

        QuestLogger qLogger = QuestServices.inst().getQuestLogger(uid, now);
        qLogger.log(new ParolePenguinRecord(pLevel));

        //=======================================================
        // Not current implement
        // Haven't design add gold (or coin) for sell penguin

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.SUCCESS, true);

        return result;
    }

    public Map<String, Object> buyPenguinAction(long timeNow, String uid, String coteId, String itemId, int nPeng)
            throws PGException {
        User user = User.getUser(uid);
        PGException.Assert(user.cotes().contains(coteId), PGError.NOT_USER_COTE,
                coteId + " not belong to user " + uid);

        // Check number of Peng in cote 
        Cote cote = Cote.getCote(uid, coteId);

        int maxPenguin = PGConfig.inst().getCote().get(cote.getLevel()).getMaxPenguin();
        PGException.Assert(cote.penguins().size() + nPeng <= maxPenguin, PGError.NOT_ENGOUH_PENGUIN_SLOT,
                "Cote hasn't enough slot for new penguin");

        QuestLogger userQLogger = QuestServices.inst().getQuestLogger(uid, timeNow);
        Penguindex penguindex = Penguindex.getPenguindex(uid);
        String[] pengIds = ShopServices.inst().buyPenguins(timeNow, user, userQLogger, penguindex, cote.penguins(),
                itemId, nPeng);

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.PENGUIN_ID_LIST, AMFBuilder.toAMF(pengIds));

        user.saveToDB();

        return result;
    }

    public Map<String, Object> buyFishAction(String userId, String itemId, long now) throws PGException {
        // Check user data with item value
        User user = User.getUser(userId);
        user.updateFromDB();

        QuestLogger userQLogger = QuestServices.inst().getQuestLogger(userId, now);
        Boolean canBuy = ShopServices.inst().buyFish(user, userQLogger, itemId, 1);

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.SUCCESS, canBuy);
        result.put(PGMacro.FISH, user.getFish());

        user.saveToDB();

        return result;
    }

    public Map<String, Object> buyGoldAction(String userId, String itemId, long now) throws PGException {
        // Check user data with item value
        User user = User.getUser(userId);
        user.updateFromDB();

        Boolean canBuy = ShopServices.inst().buyGold(user, itemId);

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.SUCCESS, canBuy);
        result.put(PGMacro.GOLD, user.getGold());

        user.saveToDB();

        return result;
    }

    public Map<String, Object> spawnEggAction(String uid, String coteID, List<String> penguinIDs, long now)
            throws PGException {
        User user = User.getUser(uid);
        PGException.Assert(user.cotes().contains(coteID), PGError.INVALID_COTE, "Invalid cote");

        final Cote cote = Cote.getCote(uid, coteID);
        for (String pengId : penguinIDs) {
            PGException.Assert(cote.penguins().contains(pengId), PGError.PENGUIN_NOT_IN_COTE,
                    "Penguin isn't contained in cote");
        }

        PriorityQueue<Penguin> penguins = new PriorityQueue(Math.max(penguinIDs.size(), 1),
                new Comparator<Penguin>() {
                    @Override
                    public int compare(Penguin p1, Penguin p2) {
                        long p1NextSpawnTime = PenguinServices.inst().nextSpawn(p1, cote);
                        long p2NextSpawnTime = PenguinServices.inst().nextSpawn(p2, cote);

                        return (p1NextSpawnTime > p2NextSpawnTime) ? 1
                                : ((p1NextSpawnTime == p2NextSpawnTime) ? 0 : -1);
                    }
                });

        Map<String, Object> failData = new HashMap();

        // init penguin entities
        for (String pengId : penguinIDs) {
            Penguin penguin = Penguin.getPenguin(uid, coteID, pengId);

            long nextSpawn = PenguinServices.inst().nextSpawn(penguin, cote);
            if (nextSpawn > now) {
                Map<String, Object> failPenguinData = new HashMap(2);
                failPenguinData.put(PGMacro.TIME_LAST_SPAWN, penguin.getLastSpawn());
                failPenguinData.put(PGMacro.EGG_STORE, penguin.getLastEggStorage().getValue());

                failData.put(pengId, failPenguinData);
            } else {
                penguins.add(penguin);
            }
        }

        Map<String, Object> successData = new HashMap();
        List<String> limitedEggPenguins = new LinkedList();

        // need for add egg
        BoxEgg boxEgg = BoxEgg.getBoxEgg(uid, coteID);
        Dog dog = Dog.getDog(uid, coteID);

        while (!penguins.isEmpty()) {
            Penguin penguin = penguins.poll();
            long nextSpawn = PenguinServices.inst().nextSpawn(penguin, cote);
            String spawnedEggKind = PenguinServices.inst().spawnEgg(penguin, nextSpawn);

            EggStoreServices.EggStorage eggStorage = EggStoreServices.inst().addEgg(cote, boxEgg, dog,
                    spawnedEggKind, now);
            if (eggStorage == EggStoreServices.EggStorage.LIMITED) {
                limitedEggPenguins.add(penguin.getPenguinID());
            }

            penguin.setLastEggStorage(eggStorage);
            penguin.saveToDB();

            Map<String, Object> penguinResp = new HashMap();
            penguinResp.put(PGMacro.KIND, spawnedEggKind);
            penguinResp.put(PGMacro.EGG_STORE, eggStorage.getValue());
            successData.put(penguin.getPenguinID(), penguinResp);
        }

        Map<String, Object> response = new HashMap();
        response.put(PGMacro.SUCCESS, successData);
        response.put(PGMacro.FAIL, failData);
        response.put(PGMacro.SPAWN_LIMITED_PENGUINS, AMFBuilder.toAMF(limitedEggPenguins));
        return response;
    }

    public Map<String, Object> moveEggFromCoteToInventoryAction(String uid, Map<String, Number> eggPacks, long now)
            throws PGException {
        User user = User.getUser(uid);
        Cote cote = Cote.getCote(uid, user.getLastCote());
        Map<String, Number> validEggs = EggStoreServices.inst().validateEgg(cote.eggStore(), eggPacks);

        Inventory inventory = Inventory.getInventory(uid);
        int inventoryAvail = PGConfig.inst().temp().MaxInventory() - inventory.numberItems();

        Map<String, Number> successEggs = EggStoreServices.inst().truncateEgg(validEggs, inventoryAvail);

        int nEggMoved = 0;
        for (Map.Entry<String, Number> movedEggEntry : successEggs.entrySet()) {
            nEggMoved += movedEggEntry.getValue().intValue();
        }

        if (nEggMoved > 0) {
            EggStoreServices.inst().moveEgg(cote.eggStore(), inventory.eggStore(), successEggs);

            QuestLogger qLogger = QuestServices.inst().getQuestLogger(uid, now);
            qLogger.log(new CollectEggsRecord(nEggMoved));
        }

        // build response amf
        Map<String, Object> response = new HashMap();

        // build success
        if (nEggMoved > 0) {
            response.put(PGMacro.SUCCESS_EGGS, successEggs);
        }

        // build full inventory eggs
        if (!successEggs.equals(validEggs)) {
            Map<String, Number> fullInvEggs = EggStoreServices.inst().substractEggs(validEggs, successEggs);

            response.put(PGMacro.FULL_INVENTORY_EGGS, fullInvEggs);
        }

        // build failed eggs
        if (!validEggs.equals(eggPacks)) {
            Map<String, Number> failedEggs = EggStoreServices.inst().substractEggs(eggPacks, validEggs);

            response.put(PGMacro.FAILED_EGGS, failedEggs);
        }

        return response;
    }

    public Map<String, Object> moveEggFromBoxEggToInventoryAction(String uid, Map<String, Number> eggPacks)
            throws PGException {
        User user = User.getUser(uid);
        BoxEgg boxEgg = BoxEgg.getBoxEgg(uid, user.getLastCote());

        int nEggMoved = 0;
        for (Map.Entry<String, Number> movedEggEntry : eggPacks.entrySet()) {
            int nEgg = movedEggEntry.getValue().intValue();

            nEggMoved += nEgg;
        }

        Inventory inventory = Inventory.getInventory(uid);
        int nEggAfterMoved = inventory.eggStore().nOfEggs() + nEggMoved;
        PGException.Assert(nEggAfterMoved <= PGConfig.inst().temp().MaxInventory(),
                PGError.NOT_ENOUGH_INVENTORY_SLOT, String.format("Not enough inventory slot; max %d; need %d",
                        PGConfig.inst().temp().MaxInventory(), nEggAfterMoved));

        EggStoreServices.inst().moveEgg(boxEgg.eggStore(), inventory.eggStore(), eggPacks);

        return AMFBuilder.toAMF(eggPacks);
    }

    public Map<String, Object> sellEggsFromInventoryAction(String userId, Map<String, Number> soldEggPacks,
            long now) throws PGException {
        User user = User.getUser(userId);
        int totalEggPrice = EggStoreServices.inst().removeEggFromStore(user.inventory().eggStore(), soldEggPacks);

        user.increaseGold(totalEggPrice);
        user.saveToDB();

        int nEggSold = 0;
        for (Map.Entry<String, Number> soldEggEntry : soldEggPacks.entrySet()) {
            int nEgg = soldEggEntry.getValue().intValue();

            nEggSold += nEgg;
        }
        QuestLogger logger = QuestServices.inst().getQuestLogger(userId, now);
        logger.log(new SellEggRecord(nEggSold));

        Map<String, Object> response = new HashMap();
        response.put(PGMacro.SUCCESS, true);
        return response;
    }

    public Map<String, Object> buyExpPenguinAction(String uid, String coteID, String pengId, int nExp, long now)
            throws PGException {
        User user = User.getUser(uid);

        int nReqCoin = PGConfig.inst().temp().BuyPenguinExp_Cost();
        PGException.Assert(user.getCoin() >= nReqCoin, PGError.NOT_ENOUGH_COIN, "Not enough coin");

        PGException.Assert(user.cotes().contains(coteID), PGError.NOT_USER_COTE,
                "Cote's not contained in user cotes: " + coteID);

        PenguinList penguinList = PenguinList.getPenguinList(uid, coteID);

        PGException.Assert(penguinList.contains(pengId), PGError.PENGUIN_NOT_IN_COTE,
                "Penguin's not contained in cote: " + pengId);

        Penguin penguin = Penguin.getPenguin(uid, coteID, pengId);
        CFPenguin.Group grConf = PGConfig.inst().getPenguin().getGroup(penguin.getKind());
        CFPenguin.Group.Level conf = grConf.get(penguin.getLevel() + 1);

        PGException.Assert(conf != null, PGError.MAX_LEVEL_PENGUIN, "Penguin's level is max");
        /*
        PGException.Assert(penguin.getExp() + nExp <= conf.getExp() ||
            grConf.containsKey(penguin.getLevel() + 2),
            PGError.MAX_LEVEL_PENGUIN,
            "Penguin's level is max");*/

        PenguinServices.inst().increasePenguinExp(uid, penguin, nExp, now);
        UserServices.inst().decreaseCoin(user, nReqCoin);

        penguin.saveToDB();
        user.saveToDB();

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.PENGUIN, penguin.buildAMF());
        Map<String, Object> userData = new HashMap();
        userData.put(PGMacro.COIN, user.getCoin());
        result.put(PGMacro.USER, userData);

        return result;
    }

    public Map<String, Object> buyLevelPenguinAction(String uid, String coteID, String pengId, long now)
            throws PGException {
        User user = User.getUser(uid);

        PGException.Assert(user.cotes().contains(coteID), PGError.NOT_USER_COTE,
                "Cote's not contained in user cotes: " + coteID);

        PenguinList penguinList = PenguinList.getPenguinList(uid, coteID);

        PGException.Assert(penguinList.contains(pengId), PGError.PENGUIN_NOT_IN_COTE,
                "Penguin's not contained in cote: " + pengId);

        Penguin penguin = Penguin.getPenguin(uid, coteID, pengId);
        CFPenguin.Group grConf = PGConfig.inst().getPenguin().getGroup(penguin.getKind());
        CFPenguin.Group.Level conf = grConf.get(penguin.getLevel() + 1);

        PGException.Assert(conf != null, PGError.MAX_LEVEL_PENGUIN, "Penguin's level is max");

        int incExp = conf.getExp() - penguin.getExp();
        int nReqCoin = 1 + ((incExp - 1) / PGConfig.inst().temp().BuyPenguinLevel_ExpPerCoin());

        PGException.Assert(user.getCoin() >= nReqCoin, PGError.NOT_ENOUGH_COIN, "Not enough coin");

        PenguinServices.inst().increasePenguinExp(uid, penguin, incExp, now);
        UserServices.inst().decreaseCoin(user, nReqCoin);

        penguin.saveToDB();
        user.saveToDB();

        Map<String, Object> result = new HashMap<String, Object>();
        result.put(PGMacro.PENGUIN, penguin.buildAMF());
        Map<String, Object> userData = new HashMap();
        userData.put(PGMacro.COIN, user.getCoin());
        result.put(PGMacro.USER, userData);

        return result;
    }

    public Map<String, Object> renameCote(String uid, String coteID, String newName) {
        PGException.Assert(newName != null, PGError.NULL_COTE_NAME, "Cote " + coteID + " cannot be set to null");

        Cote cote = Cote.getCote(uid, coteID);
        cote.setCoteName(newName);
        cote.saveToDB();

        Map<String, Object> resp = new HashMap();
        resp.put(PGMacro.SUCCESS, true);
        return resp;
    }

    public Map<String, Object> wakeDogUpAction(String uid, String wakeItemID, long now) {
        CFCote.Dog.Item wakeItem = PGConfig.inst().getCote().getDog().get(wakeItemID);
        PGException.Assert(wakeItem != null, PGError.INVALID_ITEM, "Wake item " + wakeItemID + " are invalid");

        User user = User.getUser(uid);
        switch (wakeItem.getPaymentType()) {
        case CFCote.Dog.Item.PAYMENT_TYPE_GOLD:
            PGException.Assert(user.getGold() >= wakeItem.getPrice(), PGError.NOT_ENOUGH_GOLD,
                    "Not enough gold (" + user.getGold() + "/" + wakeItem.getPrice() + ")");
            break;
        case CFCote.Dog.Item.PAYMENT_TYPE_COIN:
            PGException.Assert(user.getCoin() >= wakeItem.getPrice(), PGError.NOT_ENOUGH_COIN,
                    "Not enough gold (" + user.getCoin() + "/" + wakeItem.getPrice() + ")");
            break;
        default:
            PGException.Assert(false, PGError.UNDEFINED, "Invalid payment " + wakeItem.getPaymentType());
            break;
        }

        String coteID = user.getLastCote();
        Dog dog = Dog.getDog(uid, coteID);
        final long nextSleepTime = Math.max(now, dog.getNextSleep()) + wakeItem.getTime();
        PGException.Assert(nextSleepTime <= now + PGConfig.inst().temp().MaxDogTime(), PGError.DOG_MAX_AWAKE_TIME,
                "Dog max awake time");

        switch (wakeItem.getPaymentType()) {
        case CFCote.Dog.Item.PAYMENT_TYPE_GOLD:
            QuestLogger qLogger = QuestServices.inst().getQuestLogger(uid, now);
            UserServices.inst().decreaseGold(user, qLogger, wakeItem.getPrice());
            break;
        case CFCote.Dog.Item.PAYMENT_TYPE_COIN:
            UserServices.inst().decreaseCoin(user, wakeItem.getPrice());
            break;
        }

        dog.setNextSleep(nextSleepTime);
        UserTempData uTempData = UserTempData.getTempData(uid);
        uTempData.setData(PGMacro.WAKE_DOG_FIRST_TIME, true);

        dog.saveToDB();
        user.saveToDB();

        Map<String, Object> response = new HashMap();
        response.put(PGMacro.NEXT_SLEEP, nextSleepTime);
        return response;
    }

    public Map<String, Object> acceptDailyQuestAction(String uid, int questIndex, long now) {
        DailyQuest dailyQuest = DailyQuest.getQuest(uid, now);
        PGException.Assert(dailyQuest.getCurrentIndex() == questIndex, PGError.QUEST_WAS_NOT_ACCEPTED,
                "Quest " + questIndex + " doesn't current; current is " + dailyQuest.getCurrentIndex());

        PGException.Assert(dailyQuest.getCurrentState() == QuestState.NEW, PGError.ACCEPT_NOT_NEW_QUEST,
                "Quest " + questIndex + " must be new; current state: " + dailyQuest.getCurrentState());

        EntityContext context = EntityContext.getContext(uid);
        QuestServices.inst().acceptDailyQuest(dailyQuest, context);

        context.saveToDB();
        dailyQuest.saveToDB();

        Map<String, Object> response = new HashMap();
        response.put(PGMacro.SUCCESS, true);
        return response;
    }

    public Map<String, Object> returnDailyQuestAction(String uid, int questIndex, long now) {
        DailyQuest dailyQuest = DailyQuest.getQuest(uid, now);
        PGException.Assert(dailyQuest.getCurrentIndex() == questIndex, PGError.QUEST_WAS_NOT_ACCEPTED,
                "Quest " + questIndex + " doesn't current; current is " + dailyQuest.getCurrentIndex());

        PGException.Assert(dailyQuest.getCurrentState() == QuestState.ACCEPTED, PGError.QUEST_WAS_NOT_ACCEPTED,
                "Quest " + questIndex + " must be accepted for return; current state: "
                        + dailyQuest.getCurrentState());

        EntityContext context = EntityContext.getContext(uid);
        Map<String, Object> pzDesc = QuestServices.inst().returnDailyQuest(dailyQuest, context, now);

        context.saveToDB();
        dailyQuest.saveToDB();

        return pzDesc;
    }

    public Map<String, Object> completeDailyQuestImmediately(String uid, int questIndex, long now) {
        DailyQuest dailyQuest = DailyQuest.getQuest(uid, now);
        PGException.Assert(dailyQuest.getCurrentIndex() == questIndex, PGError.QUEST_WAS_NOT_ACCEPTED,
                "Quest " + questIndex + " doesn't current; current is " + dailyQuest.getCurrentIndex());

        PGException.Assert(dailyQuest.getCurrentState() == QuestState.ACCEPTED, PGError.QUEST_WAS_NOT_ACCEPTED,
                "Quest " + questIndex + " must be accepted for complete; current state: "
                        + dailyQuest.getCurrentState());

        User user = User.getUser(uid);
        final int completeImmCost = PGConfig.inst().getDailyQuest().get(questIndex)
                .getCompleteImmCost(user.getLevel());

        PGException.Assert(user.getCoin() >= completeImmCost, PGError.NOT_ENOUGH_COIN,
                "Not enough coin for complete quest immedately; Require: " + user.getCoin() + " have: "
                        + completeImmCost);

        EntityContext context = EntityContext.getContext(user);
        Map<String, Object> pzDesc = QuestServices.inst().completeImmediatelyDailyQuest(dailyQuest, context, now);

        UserServices.inst().decreaseCoin(user, completeImmCost);

        context.saveToDB();
        dailyQuest.saveToDB();

        return pzDesc;
    }

    public Map<String, Object> returnMainQuestAction(String uid, String qLine, long now) {
        MainQuestLine questLine = MainQuestLine.getQuestLine(uid, qLine);

        PGException.Assert(questLine.getState() == QuestState.ACCEPTED, PGError.QUEST_WAS_NOT_ACCEPTED,
                "You haven't accepted quest " + qLine + "{" + questLine.getIndex() + "} yet!");

        EntityContext context = EntityContext.getContext(uid);
        PGException.Assert(questLine.getChecker().isAccept(context), PGError.INCOMPLETED_QUEST,
                "You not have enough resource for return this quest");

        questLine.getChecker().returnQuest(context);
        Map<String, Object> prizeDesc = questLine.getPrize().award(context, now);

        QuestLogger qLogger = QuestServices.inst().getQuestLogger(uid, now);
        qLogger.log(new CompletedMainQuestRecord());

        questLine.setState(QuestState.RETURNED);

        CFMainQuests.QuestLine qLineConf = PGConfig.inst().getMainQuest().get(qLine);

        // get new main quest
        int currentMinLevel = qLineConf.minimizeLevel(context.getUser().getLevel());
        int lastAcceptMinLevel = qLineConf.minimizeLevel(questLine.getLastAcceptLevel());
        if (currentMinLevel > lastAcceptMinLevel) {
            questLine.setIndex(0);
            questLine.setLastAcceptLevel(context.getUser().getLevel());
            questLine.setState(QuestState.ACCEPTED);
        } else if (questLine.getIndex() + 1 < qLineConf.get(currentMinLevel).size()) {
            questLine.setIndex(questLine.getIndex() + 1);
            questLine.setLastAcceptLevel(context.getUser().getLevel());
            questLine.setState(QuestState.ACCEPTED);
        }

        context.saveToDB();
        questLine.saveToDB();

        return prizeDesc;
    }

    public Map<String, Object> takeSnapshotAction(String uid, long now) {
        UserDailyData uDailyData = UserDailyData.getData(uid, now);
        boolean isTakenSnapshot = Boolean.parseBoolean(uDailyData.getData(PGMacro.TAKEN_SNAPSHOT));
        if (!isTakenSnapshot) {
            User user = User.getUser(uid);
            UserServices.inst().increaseCoin(user, PGConfig.inst().temp().TakeSnapshotFirstTimeInDay_Prize());
            user.saveToDB();
            uDailyData.setData(PGMacro.TAKEN_SNAPSHOT, true);
        }

        QuestLogger questLogger = QuestServices.inst().getQuestLogger(uid, now);
        TakeSnapshotQuestRecord qRecord = new TakeSnapshotQuestRecord();
        questLogger.log(qRecord);

        Map<String, Object> resp = new HashMap(1);
        resp.put(PGMacro.TAKEN_SNAPSHOT, isTakenSnapshot);
        return resp;
    }

    public Map<String, Object> setUIStateAction(String uid, Map<String, Number> tuts) {
        UIData tut = UIData.getEntity(uid);
        tut.setStates(tuts);

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> saveSettingsAction(String uid, Map<String, Object> settings) {
        UserSettings userSettings = UserSettings.getEntity(uid);
        userSettings.setStates((Map) settings);

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> getMailsAction(String uid, int offset, int length) {
        MailBox mailBox = MailBox.getMailBoxOf(uid);
        mailBox.resetUnreadMail();
        return MailServices.inst().buildMailBox(mailBox, offset, length);
    }

    public Map<String, Object> clearAllMailsAction(String uid) {
        MailBox mailBox = MailBox.getMailBoxOf(uid);
        MailServices.inst().clearAll(mailBox);

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> getUserGiftsAction(String uid) {
        UserGifts userGifts = UserGifts.getGift(uid);
        return userGifts.buildAMF();
    }

    public Map<String, Object> receiveGiftAction(String uid, String giftID, long now) {
        UserGifts userGifts = UserGifts.getGift(uid);
        PGException.Assert(userGifts.contains(giftID), PGError.INVALID_GIFT,
                "Gift " + giftID + " not be " + uid + "'s gift");

        Gift gift = Gift.getGift(giftID);
        PGPrize giftPrize = PrizeFactory.getPrize(gift.getPrizeData());

        EntityContext context = EntityContext.getContext(uid);
        Map<String, Object> prizeDesc = giftPrize.award(context, now);

        context.saveToDB();
        userGifts.remove(giftID);

        return prizeDesc;
    }

    public Map<String, Object> destroyGiftAction(String uid, String giftID, long now) {
        UserGifts userGifts = UserGifts.getGift(uid);
        PGException.Assert(userGifts.contains(giftID), PGError.INVALID_GIFT,
                "Gift " + giftID + " not be " + uid + "'s gift");

        userGifts.remove(giftID);
        Gift.destroyGift(giftID);

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> sendGiftAction(List<String> receivers, Map<String, Object> giftPrize, long now,
            int expired) {
        Gift gift = GiftServices.inst().sendGift(receivers, giftPrize, now, expired);

        Map<String, Object> resp = new HashMap();
        resp.put(PGMacro.GIFT_ID, gift.getGiftID());
        return resp;
    }

    public Map<String, Object> sendGiftToAllUsersAction(Map<String, Object> giftPrize, long now, int expired) {
        Collection<String> allUsers = UserServices.inst().getAllUsers();

        Gift gift = GiftServices.inst().sendGift(allUsers, giftPrize, now, expired);

        Map<String, Object> resp = new HashMap();
        resp.put(PGMacro.GIFT_ID, gift.getGiftID());
        return resp;
    }

    public Map<String, Object> loginAwardAction(String uid, long now) {
        UserDailyData uDailyData = UserDailyData.getData(uid, now);
        PGException.Assert(!Boolean.parseBoolean(uDailyData.getData(PGMacro.RECEIVED_LOGIN_PRIZE)),
                PGError.RECEIVED_LOGIN_PRIZE, "You have received this prize");

        UserTempData uTempData = UserTempData.getTempData(uid);
        int nRepLoginDay = PGHelper.toInteger(uTempData.getData(PGMacro.REPEATED_LOGIN_DAY));

        // prizing
        Map<String, Object> prizeDesc = Collections.EMPTY_MAP;
        if (nRepLoginDay > 0) {
            PGPrize dailyLoginPrize = PrizeFactory
                    .getPrize(PGConfig.inst().getPrizing().dailyLogin().prizeForDay(nRepLoginDay));

            EntityContext context = EntityContext.getContext(uid);
            prizeDesc = dailyLoginPrize.award(context, now);
            context.saveToDB();
        }

        uDailyData.setData(PGMacro.RECEIVED_LOGIN_PRIZE, true);

        Map<String, Object> resp = new HashMap(2);
        resp.put(PGMacro.REPEATED_LOGIN_DAY, nRepLoginDay);
        resp.put(PGMacro.PRIZE, prizeDesc);
        return resp;
    }

    public Map<String, Object> getAchievemensAction(String uid, long now) {
        EntityContext context = EntityContext.getContext(uid);
        return QuestServices.inst().buildAchievementsAMF(context, now);
    }

    public Map<String, Object> receiveAchievementPrizeAction(String uid, String achID, String medalID, long now) {
        Achievement achivement = Achievement.getAchievements(uid, achID);
        EntityContext context = EntityContext.getContext(uid);

        PGException.Assert(PGConfig.inst().getAchievements().get(achID).isEnable(), PGError.ACHIEVEMENT_DISABLED,
                "Achievement are disabled");
        PGException.Assert(!achivement.isReceivedPrize(medalID), PGError.RECEIVED_ACHIEVEMENT,
                "You have received this prize");
        PGException.Assert(achivement.getChecker(medalID).isAccept(context), PGError.INCOMPLETED_BEFORE_QUEST,
                "Incompleted achivement");

        Map<String, Object> prizeDesc = achivement.getPrize(medalID).award(context, now);
        context.saveToDB();

        achivement.setReceivedPrize(medalID);
        return prizeDesc;
    }

    public Map<String, Object> takeRandomizePrizeAction(String uid, long now) {
        UserTempData uTempData = UserTempData.getTempData(uid);

        int nTurn = PGHelper.toInteger(uTempData.getData(PGMacro.RAND_PRIZE_TURN));
        PGException.Assert(nTurn > 0, PGError.NOT_ENOUGH_RP_TURN, "You have 0 turn");

        // reduce turn
        --nTurn;
        uTempData.setData(PGMacro.RAND_PRIZE_TURN, nTurn);

        String prizeID = PGConfig.inst().getRandomizePrizes().randomPrize();

        CFRandomizePrize.Prize prizeData = PGConfig.inst().getRandomizePrizes().get(prizeID);
        if (prizeData.isAutoPrize()) {
            PGPrize prize = PrizeFactory.getPrize(prizeData.getPrize());
            EntityContext context = EntityContext.getContext(uid);
            Map<String, Object> pzDesc = prize.award(context, now);
            context.saveToDB();

            // find total gold prized:
            Deque<Map<String, Object>> pzStack = new ArrayDeque();
            int totalGoldPrized = 0;
            pzStack.add(prizeData.getPrize());
            while (!pzStack.isEmpty()) {
                Map<String, Object> pz = pzStack.pollLast();
                for (Map.Entry<String, Object> pzEntry : pz.entrySet()) {
                    String pzKey = pzEntry.getKey();
                    Object pzVal = pzEntry.getValue();

                    if (pzVal instanceof Map) {
                        pzStack.addLast((Map) pzVal);
                    } else if ("gold".equals(pzKey)) {
                        totalGoldPrized += PGHelper.toInteger(pzVal);
                    }
                }
            }

            if (totalGoldPrized > 0) {
                QuestLogger qLogger = QuestServices.inst().getQuestLogger(uid, now);
                qLogger.log(new GoldDialRecord(totalGoldPrized));
            }

            return AMFBuilder.make(PGMacro.RAND_PRIZE_ID, prizeID, PGMacro.PRIZE, pzDesc);
        } else {
            String giftID = GiftServices.inst().sendGift(Arrays.asList(new String[] { uid }), prizeData.getPrize(),
                    now, PGConfig.inst().temp().RandomizePrize_Expire()).getGiftID();

            return AMFBuilder.make(PGMacro.RAND_PRIZE_ID, prizeID, PGMacro.GIFT_ID, giftID);
        }
    }

    public Map<String, Object> buyRandomizePrizeTurnAction(String uid, String itemID, long now) {
        CFRandomizePrize.Items itemsConf = PGConfig.inst().getRandomizePrizes().getItems();
        CFRandomizePrize.Items.Item item = itemsConf.get(itemID);
        PGException.Assert(item != null, PGError.INVALID_ITEM, "Invalid item");

        User user = User.getUser(uid);
        UserDailyData uDaily = UserDailyData.getData(uid, now);
        if (PGMacro.GOLD.equals(item.getPaymentType())) {
            int nTurnHaveBought = PGHelper.toInteger(PGMacro.RP_TURN_BOUGHT_BY_GOLD);

            PGException.Assert(nTurnHaveBought < PGConfig.inst().temp().MaxBuyRPByGoldPerDay(),
                    PGError.CANNOT_BUY_RP_TURN, "You have bought maximum turn by gold in day");

            PGException.Assert(user.getGold() >= item.getPrice(), PGError.NOT_ENOUGH_GOLD, "Not enough gold");

            QuestLogger uQuestLogger = QuestServices.inst().getQuestLogger(uid, now);
            UserServices.inst().decreaseGold(user, uQuestLogger, item.getPrice());
            uDaily.increaseData(PGMacro.RP_TURN_BOUGHT_BY_GOLD, item.getnTurn());
        } else if (PGMacro.COIN.equals(item.getPaymentType())) {
            int nTurnHaveBought = PGHelper.toInteger(PGMacro.RP_TURN_BOUGHT_BY_COIN);

            PGException.Assert(nTurnHaveBought < PGConfig.inst().temp().MaxBuyRPByCoinPerDay(),
                    PGError.CANNOT_BUY_RP_TURN, "You have bought maximum turn by coin in day");

            PGException.Assert(user.getCoin() >= item.getPrice(), PGError.NOT_ENOUGH_COIN, "Not enough coin");

            UserServices.inst().decreaseCoin(user, item.getPrice());
            uDaily.increaseData(PGMacro.RP_TURN_BOUGHT_BY_COIN, item.getnTurn());
        } else {
            PGException.Assert(false, PGError.INVALID_ITEM, "Invalid payment " + item.getPaymentType());
        }

        UserTempData uTempData = UserTempData.getTempData(uid);
        int nTurn = PGHelper.toInteger(uTempData.getData(PGMacro.RAND_PRIZE_TURN));
        nTurn += item.getnTurn();
        uTempData.setData(PGMacro.RAND_PRIZE_TURN, nTurn);

        user.saveToDB();

        return AMFBuilder.make(PGMacro.RAND_PRIZE_TURN, nTurn);
    }

    public Map<String, Object> reloadFriendListAction(String uid, String signedRequest, long now)
            throws ZingMeApiException, IOException {
        UserTempData uTempData = UserTempData.getTempData(uid);
        long lastSync = PGHelper.toLong(uTempData.getData(PGMacro.LAST_TIME_SYNC_FRIEND_LIST));

        PGException.Assert(lastSync + PGConfig.inst().temp().SyncFriendsCooldown() < now,
                PGError.SYNC_FRIEND_ARE_LOCKED, "Sync friend are locked (unlock - " + lastSync
                        + PGConfig.inst().temp().SyncFriendsCooldown() + ")");

        SNServices sns = new SNServices(signedRequest);
        PGException.Assert(sns.validUser(uid), PGError.INVALID_SIGNED_REQUEST, "Signed request are invalid");
        FriendList friendList = FriendList.getFriendList(uid);

        FriendServices.inst().reloadFriendList(friendList, sns, uTempData, now);

        return friendList.buildAMF();
    }

    public Map<String, Object> wakeDogFirstTimeAction(String uid, String coteID, long now) {
        UserTempData uTempData = UserTempData.getTempData(uid);
        boolean isWakenDogFirstTime = Boolean.parseBoolean(uTempData.getData(PGMacro.WAKE_DOG_FIRST_TIME));
        PGException.Assert(!isWakenDogFirstTime, PGError.HAVE_WAKEN_DOG_FIRST_TIME,
                "You have waken dog first time");

        Dog dog = Dog.getDog(uid, coteID);
        PGException.Assert(!dog.isAwake(now), PGError.HAVE_WAKEN_DOG_FIRST_TIME, "The dog is awaken right now");

        final long nextSleepTime = Math.max(now, dog.getNextSleep())
                + PGConfig.inst().temp().WakeDogFirstTime_Time();
        PGException.Assert(nextSleepTime <= now + PGConfig.inst().temp().MaxDogTime(), PGError.DOG_MAX_AWAKE_TIME,
                "Dog max awake time");

        dog.setNextSleep(nextSleepTime);
        uTempData.setData(PGMacro.WAKE_DOG_FIRST_TIME, true);
        dog.saveToDB();

        Map<String, Object> resp = new HashMap(1);
        resp.put(PGMacro.NEXT_SLEEP, nextSleepTime);
        return resp;
    }

    public Map<String, Object> takeAdsAction(String uid, long now) {
        UserDailyData uDailyData = UserDailyData.getData(uid, now);
        boolean isTakenAds = Boolean.parseBoolean(uDailyData.getData(PGMacro.TAKEN_ADS));
        PGException.Assert(!isTakenAds, PGError.HAVE_TAKEN_ADS_TODAY, "You have taken ads today");

        uDailyData.setData(PGMacro.TAKEN_ADS, true);

        EntityContext context = EntityContext.getContext(uid);

        Map<String, Object> prizeData = PGConfig.inst().getPrizing().getAds()
                .getPrize(context.getUser().getLevel());
        PGPrize prize = PrizeFactory.getPrize(prizeData);
        Map<String, Object> prizeDesc = prize.award(context, now);
        context.saveToDB();

        return prizeDesc;
    }

    public Map<String, Object> makeGiftCodes(Map<String, Object> giftData, int giftExpire, int nCode,
            int codeExpire, long now) {
        final String templID = PGKeys.randomKey();
        GiftTemplate.newTemplate(templID, giftData, giftExpire, codeExpire);

        List<String> codes = new ArrayList(nCode);
        for (int i = 0; i < nCode; i++) {
            do {
                String code = PGKeys.randomCode();
                if (!GiftCode.isExist(code)) {
                    GiftCode.newGift(code, templID, 1, codeExpire);
                    codes.add(code);
                    break;
                }
            } while (true);
        }

        return AMFBuilder.toAMF(codes);
    }

    public Map<String, Object> useGiftCode(String uid, String code, long now) {
        try {
            GiftCode giftCode = GiftCode.getGift(code);
            if (giftCode.getRemain() > 0) {
                GiftTemplate giftTemplate = GiftTemplate.getTemplate(giftCode.getGiftTemplateID());

                List<String> receivers = new ArrayList(1);
                receivers.add(uid);

                Map<String, Object> giftPrize = (Map) JSONValue.parse(giftTemplate.getGiftData());

                Gift gift = GiftServices.inst().sendGift(receivers, giftPrize, now, giftTemplate.getGiftExpire());

                giftCode.setRemain(giftCode.getRemain() - 1);
                giftCode.saveToDB();

                return gift.buildAMF();
            }
        } catch (PGException ex) {
        }

        Map<String, Object> resp = new HashMap(1);
        resp.put(PGMacro.ERROR_CODE, PGError.INVALID_GIFT_CODE);
        return resp;
    }

    public Map<String, Object> getPaymentAction(String uid, String billNo) {
        Payment payment = Payment.getPayment(billNo);
        PGException.Assert((uid != null) && uid.equals(payment.getUid()), PGError.BILL_NO_NOT_BELONG_TO_USER,
                "Billno " + billNo + " not belong to user " + uid);

        return payment.buildAMF();
    }

    public Map<String, Object> addWhiteListAction(List<String> whiteList) {
        String[] whiteArr = whiteList.toArray(new String[whiteList.size()]);
        UserList.getList(UserList.ListType.WHITE_LIST).add(whiteArr);

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> addSystemListAction(List<String> sysList) {
        String[] sysArr = sysList.toArray(new String[sysList.size()]);
        UserList.getList(UserList.ListType.SYSTEM_ACCOUNT).add(sysArr);

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> deleteUserAction(String uid, String adminPassword) {
        String md5Pass = DigestUtils.md5Hex(adminPassword);
        PGException.Assert(PGConfig.inst().temp().SystemPasswordMD5().equals(md5Pass),
                PGError.INVALID_SIGNED_REQUEST, "Invalid password");

        try {
            UserServices.inst().destroyUser(uid);
        } catch (Exception ex) {
            PGException.pgThrow(ex);
        }

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> getGameMessagesAction(long now) {
        return GameMessageList.getMessages().buildAMF(true);
    }

    public Map<String, Object> getAllGameMessagesAction(long now) {
        return GameMessageList.getMessages().buildAMF(false);
    }

    public Map<String, Object> addGameMessagesAction(List<Map<String, Object>> data, long now) {
        String[] msgIDs = new String[data.size()];
        int i = 0;
        for (Map<String, Object> msgData : data) {
            String content = (String) msgData.get("content");
            int order = PGHelper.toInteger(msgData.get("order"));
            int expire = PGHelper.toInteger(msgData.get("expire"));

            String msgID = PGKeys.randomKey();
            GameMessage.newMsg(msgID, content, order, expire);
            msgIDs[i++] = msgID;
        }

        GameMessageList.getMessages().add(msgIDs);

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> disableGameMessagesAction(List<String> msgIDs, long now) {
        for (String msgID : msgIDs) {
            if (GameMessage.isExist(msgID)) {
                GameMessage gm = GameMessage.getMsg(msgID);
                gm.setEnable(false);
                gm.saveToDB();
            }
        }

        return Collections.EMPTY_MAP;
    }

    public Map<String, Object> visitCote(String uid, String coteID, long now) {
        EntityContext context = EntityContext.getContext(uid, coteID);
        QuestLogger uQLogger = QuestServices.inst().getQuestLogger(uid, now);
        CoteServices.inst().updateCote(context, uQLogger, now);
        context.saveToDB();

        return context.getCote().buildRescusiveAMF(true, true, true, true);
    }
}