/*
 * JBoss, Home of Professional Open Source
 * Copyright 2007, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags.
 * See the copyright.txt in the distribution for a full listing
 * of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License, v. 2.0.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * v. 2.0 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * (C) 2007,
 * @author JBoss Inc.
 */
package com.jboss.ba.demo.client;

import com.arjuna.mw.wst.UserBusinessActivity;
import com.arjuna.mw.wst.UserBusinessActivityFactory;
import com.arjuna.wst.SystemException;
import com.arjuna.wst.UnknownTransactionException;
import com.arjuna.wst.WrongStateException;
import com.arjuna.wst.TransactionRolledBackException;
import org.apache.log4j.Logger;
import com.jboss.ba.demo.service.Hotel;
import com.jboss.ba.demo.service.RoomAlreadyOccupiedException;
import com.jboss.ba.demo.service.HotelCustomException;
import com.jboss.ba.demo.service.MealOrder;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * This is client of the Hotel Web Service.
 *
 * @author Maciej P. Machulak (mmachulak@redhat.com)
 * @version 0.1
 */
public class HotelService extends HttpServlet
{

    // Logger
    private static Logger log = Logger.getLogger(HotelService.class);

    // Hotel BA
    private static Hotel hotel;

    private ServletContext context;

    public void init(final ServletConfig config) throws ServletException
    {
        hotel = this.getPortHotel();
        context = config.getServletContext();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        processRequest(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        processRequest(request,response);
    }

    private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        log.info("processRequest()");

        // Get session
        HttpSession theSession = request.getSession(true);

        // Get current state
        Integer state = (Integer) theSession.getAttribute("state");
        if ( state == null)
        {
            state = 0;
        }
        log.info("Current state: " + state);

        String address = "/index.jsp";

        String continuePage = request.getParameter("continue");
        if (continuePage != null)
        {
            address = "/" + continuePage;
        }

        // Get business activity
        String activeResponse = "";
        UserBusinessActivity uba = (UserBusinessActivity) theSession.getAttribute("uba");
        if (uba != null)
        {
            activeResponse = "<div id=\"headerActive\"><p align=\"center\">Business Activity is ACTIVE (";
            activeResponse += uba.toString();
            activeResponse += ")</p></div>";
        }

        // Check if this is access to the jsp page
        String requestType = request.getParameter("page");
        if (requestType != null)
        {
            request.setAttribute("ACTIVE",activeResponse);
            address = "/" + requestType;
        }
        else
        {
            // Check if BA is request
            String baRequest = request.getParameter("baRequest");
            log.info("Business Activity request: " + baRequest);

            if (baRequest == null)
            {
                baRequest = "false";
            }

            // Get method
            String methodName = request.getParameter("method");
            log.info("Method name: " + methodName);

            if (methodName == null)
            {
                methodName = "init";
            }

            // Get parameters
            String[] parameters = request.getParameterValues("parameters");
            log.info("Parameters: " + parameters);

            // If there is not business activity then
            String methodResponse = "";
            try
            {
                if (baRequest.equals("true"))
                {
                    if (state == 0)
                    {
                        log.info("Data not initialised");
                        methodResponse = "You need to initialise data first.";
                    }
                    else
                    {
                        if (uba == null || uba.toString().equals("Unknown"))
                        {
                            if (methodName.equals("startBA"))
                            {
                                log.info("Starting Business Activity");
                                resetView();

                                UserBusinessActivity businessActivity = UserBusinessActivityFactory.userBusinessActivity();
                                startBusinessActivity(businessActivity);
                                theSession.setAttribute("uba",businessActivity);
                                methodResponse = "Business Activity started: " + businessActivity.toString();
                                log.info("Business Activity started: " + businessActivity.toString());
                                activeResponse = "<div id=\"headerActive\"><p align=\"center\">Business Activity is ACTIVE (";
                                activeResponse += businessActivity.toString();
                                activeResponse += ")</p></div>";
                                state = 2;
                            }
                            else
                            {
                                log.info("Business Activity not started");
                                methodResponse = "You need to start the Business Activity first.";
                            }
                        }
                        else
                        {
                            if (methodName.equals("getHotelInfo"))
                            {
                                methodResponse = getHotelInfo();
                            }
                            else if (methodName.equals("getHotelOrders"))
                            {
                                methodResponse = getHotelOrders();
                            }
                            else if (methodName.equals("increaseHotelRating"))
                            {
                                if (increaseHotelRating(parameters))
                                {
                                    methodResponse = "true";
                                }
                                else
                                {
                                    methodResponse = "false";
                                }
                            }
                            else if (methodName.equals("decreaseHotelRating"))
                            {
                                if (decreaseHotelRating(parameters))
                                {
                                    methodResponse = "true";
                                }
                                else
                                {
                                    methodResponse = "false";
                                }
                            }
                            else if (methodName.equals("bookRoom"))
                            {
                                methodResponse = bookRoom(parameters).toString();
                            }
                            else if (methodName.equals("bookFitnessPass"))
                            {
                                if (bookFitnessPass(parameters))
                                {
                                    methodResponse = "true";
                                    state = 3;
								}
                                else
                                {
                                    methodResponse = "false";
                                }
                            }
                            else if (methodName.equals("getComplimentaryPackageNumber"))
                            {
                                methodResponse = getComplimentaryPackageNumber(parameters).toString();
                            }
                            else if (methodName.equals("getCasinoToken"))
                            {
                                methodResponse = getCasinoToken(parameters).toString();
                            }
                            else if (methodName.equals("orderMeal"))
                            {
                                methodResponse = orderMeal(parameters).toString();
                            }
                            else if (methodName.equals("orderExtraMiniBar"))
                            {
                                if (orderExtraMiniBar(parameters))
                                {
                                    methodResponse = "true";
                                }
                                else
                                {
                                    methodResponse = "false";
                                }
                            }
                            else if (methodName.equals("completeBA"))
                            {
                                completeBusinessActivity(uba);
                                methodResponse = "Transaction completed successfully.";
                                state = 2;
                            }
                            else if (methodName.equals("closeBA"))
                            {
                                closeBusinessActivity(uba);
                                theSession.removeAttribute("uba");
                                activeResponse = "";
                                methodResponse = "Transaction closed successfully.";
                                state = 1;
                            }
                            else if (methodName.equals("cancelBA"))
                            {
                                cancelBusinessActivity(uba);
                                theSession.removeAttribute("uba");
                                activeResponse = "";
                                methodResponse = "Transaction cancelled successfully.";
                                state = 1;
                            }
                            else
                            {
                                methodResponse = "Incorrect method";
                            }

                        }
                    }
                }
                else
                {
                    if (methodName.equals("initialiseData"))
                    {
                        if (state == 0)
                        {
                            String[][] users = new String[3][2];
                            users[0][0] = request.getParameter("username1");
                            users[0][1] = request.getParameter("password1");
                            users[1][0] = request.getParameter("username2");
                            users[1][1] = request.getParameter("password2");
                            users[2][0] = request.getParameter("username3");
                            users[2][1] = request.getParameter("password3");
                            String numberOfRoomsString = request.getParameter("numberOfRooms");
                            initialiseData(users,new Integer(numberOfRoomsString));
                            methodResponse = getData();
                            state = 1;
                        }
                        else
                        {
                            methodResponse = "Data has been already initialised.";
                        }

                    }
                    else if (methodName.equals("startBA"))
                    {
                        log.info("Business Activity is already started");
                        methodResponse = "Business Activity is already started.";
                    }
                    else if (methodName.equals("dropHotelData"))
                    {
                        if (state != 0)
                        {
                            dropHotelData();
                            state = 0;
                        }
                        else
                        {
                            methodResponse = "Data has not been initialised.";
                        }
                    }
                    else if (methodName.equals("getHotelData"))
                    {
                        if (state != 0)
                        {
                            methodResponse = getData();
                        }
                        else
                        {
                            methodResponse = "Data has not been initialised.";
                        }
                    }
                    else if (methodName.equals("cancelRoom"))
                    {
                        if (cancelRoom(parameters))
                        {
                            methodResponse = "true";
                        }
                        else
                        {
                            methodResponse = "false";
                        }
                    }
                    else if (methodName.equals("cancelFitnessPass"))
                    {
                        if (cancelFitnessPass(parameters))
                        {
                            methodResponse = "true";
                        }
                        else
                        {
                            methodResponse = "false";
                        }
                    }
                    else if (methodName.equals("cancelComplimentaryPackageNumber"))
                    {
                        if (cancelComplimentaryPackageNumber(parameters))
                        {
                            methodResponse = "true";
                        }
                        else
                        {
                            methodResponse = "false";
                        }
                    }
                    else if (methodName.equals("cancelCasinoToken"))
                    {
                        if (cancelCasinoToken(parameters))
                        {
                            methodResponse = "true";
                        }
                        else
                        {
                            methodResponse = "false";
                        }
                    }
                    else if (methodName.equals("cancelMeal"))
                    {
                        if (cancelMeal(parameters))
                        {
                            methodResponse = "true";
                        }
                        else
                        {
                            methodResponse = "false";
                        }
                    }
                    else if (methodName.equals("cancelExtraMiniBar"))
                    {
                        if (cancelExtraMiniBar(parameters))
                        {
                            methodResponse = "true";
                        }
                        else
                        {
                            methodResponse = "false";
                        }
                    }
                    else if (methodName.equals("init"))
                    {
                        address = "/index.jsp";
                    }
                    else
                    {
                        methodResponse = "Incorrect method";
                    }
                }
            }
            catch (TransactionRolledBackException trbe)
            {
                trbe.printStackTrace();
                methodResponse = "Transaction Rolled Back Exception thrown:<br>";
                methodResponse = methodResponse + trbe.getMessage();
                activeResponse = "";
            }
            catch (Exception e)
            {
                e.printStackTrace();
                methodResponse = "Exception thrown:<br>";
                methodResponse = methodResponse + e.getMessage();
            }

            request.setAttribute("DEBUG",methodResponse);
            request.setAttribute("ACTIVE",activeResponse);
        }
        // Activate appropriate links
        String initialiseResponse = "";
        String dropResponse = "";
        String showResponse = "";
        String orderResponse = "";
        String beginResponse = "";
        String baserviceResponse = "";
        String completeResponse = "";
        String closeResponse = "";
        String cancelResponse = "";
        String serviceResponse = "";
        if ( state == 0 )
        {
            // Application just started
            initialiseResponse = "";
            dropResponse = "/><a";
            showResponse = "/><a";
            orderResponse = "/><a";
            beginResponse = "/><a";
            baserviceResponse = "/><a";
            completeResponse = "/><a";
            closeResponse = "/><a";
            cancelResponse = "/><a";
            serviceResponse = "/><a";
        }
        else if ( state == 1 )
        {
            // Data has been initialised
            initialiseResponse = "/><a";
            dropResponse = "";
            showResponse = "";
            orderResponse = "/><a";
            beginResponse = "";
            baserviceResponse = "/><a";
            completeResponse = "/><a";
            closeResponse = "/><a";
            cancelResponse = "/><a";
            serviceResponse = "";
        }
        else if ( state == 2 )
        {
            // Business activity has been started
            initialiseResponse = "/><a";
            dropResponse = "/><a";
            showResponse = "";
            orderResponse = "";
            beginResponse = "/><a";
            baserviceResponse = "";
            completeResponse = "/><a";
            closeResponse = "";
            cancelResponse = "";
            serviceResponse = "/><a";
        }
        else if ( state == 3 )
        {
            // Coordinator completion method has been used
            initialiseResponse = "/><a";
            dropResponse = "/><a";
            showResponse = "";
            orderResponse = "";
            beginResponse = "/><a";
            baserviceResponse = "";
            completeResponse = "";
            closeResponse = "/><a";
            cancelResponse = "";
            serviceResponse = "/><a";
        }

        theSession.setAttribute("state",state);
        request.setAttribute("INITIALISE",initialiseResponse);
        request.setAttribute("DROP",dropResponse);
        request.setAttribute("SHOW",showResponse);
        request.setAttribute("ORDER",orderResponse);
        request.setAttribute("BEGIN",beginResponse);
        request.setAttribute("BASERVICE",baserviceResponse);
        request.setAttribute("COMPLETE",completeResponse);
        request.setAttribute("CLOSE",closeResponse);
        request.setAttribute("CANCEL",cancelResponse);
        request.setAttribute("SERVICE",serviceResponse);

        context.getRequestDispatcher(address).forward(request, response);
    }

    private void initialiseData(String[][] users, Integer numberOfRooms)
    {
        hotel.initialiseHotel(users,numberOfRooms);
    }

    private String getData()
    {
        return hotel.getHotelData();
    }

    private String getHotelOrders()
    {
        return hotel.getHotelOrders();
    }

    private void dropHotelData()
    {
        hotel.dropHotelData();
    }

    private void resetView()
    {
        hotel.resetView();
    }

    private UserBusinessActivity startBusinessActivity(UserBusinessActivity uba)
            throws SystemException, WrongStateException
    {
        // Notifying the Web Services to switch Compensation View on
        hotel.compensationView(true);

        // Starting the transaction
        uba.begin();
        return uba;
    }

    private void completeBusinessActivity(UserBusinessActivity uba)
            throws SystemException, WrongStateException, UnknownTransactionException, TransactionRolledBackException
    {
        // Completeing the transaction
        uba.complete();
    }

    private void closeBusinessActivity(UserBusinessActivity uba)
            throws SystemException, WrongStateException, UnknownTransactionException, TransactionRolledBackException
    {
        // Closing the transaction
        uba.close();
    }

    private void cancelBusinessActivity(UserBusinessActivity uba)
            throws SystemException, WrongStateException, UnknownTransactionException
    {
        // Notifying the Web Services to switch Compensation View off
        hotel.compensationView(false);
        hotel.showCompensation();

        // Cancelling the transaction
        uba.cancel();
    }

    private String getHotelInfo()
    {
        log.info("getHotelInfo()");
        return hotel.getHotelInfo();
    }

    private boolean increaseHotelRating(String[] parameters)
    {
        log.info("increaseHotelRating()");
        Integer points = new Integer(parameters[2]);
        return hotel.increaseHotelRating(parameters[0],parameters[1],points);
    }

    private boolean decreaseHotelRating(String[] parameters)
    {
        log.info("decreaseHotelRating()");
        Integer points = new Integer(parameters[2]);
        return hotel.decreaseHotelRating(parameters[0],parameters[1],points);
    }

    private Integer bookRoom(String[] parameters)
            throws RoomAlreadyOccupiedException, HotelCustomException, NumberFormatException
    {
        log.info("bookRoom()");
        Integer roomNumber = new Integer(parameters[2]);
        return hotel.bookRoom(parameters[0],parameters[1],roomNumber);
    }

    private boolean cancelRoom(String[] parameters)
            throws RoomAlreadyOccupiedException, HotelCustomException, NumberFormatException
    {
        log.info("cancelRoom()");
        Integer reservationNumber = new Integer(parameters[2]);
        return hotel.cancelRoom(parameters[0],parameters[1],reservationNumber);
    }

    private Integer getComplimentaryPackageNumber(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("getComplimentaryPackageNumber()");
        Integer reservationNumber = new Integer(parameters[2]);
        return hotel.getComplimentaryPackageNumber(parameters[0],parameters[1],reservationNumber);

    }

    private boolean cancelComplimentaryPackageNumber(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("cancelComplimentaryPackageNumber()");
        Integer reservationNumber = new Integer(parameters[2]);
        return hotel.cancelComplimentaryPackageNumber(parameters[0],parameters[1],reservationNumber);
    }

    private boolean bookFitnessPass(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("bookFitnessPass()");
        Integer reservationNumber = new Integer(parameters[2]);
        Integer amount = new Integer(parameters[3]);
        return hotel.bookFitnessPass(parameters[0],parameters[1],reservationNumber,amount);
    }

    private boolean cancelFitnessPass(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("cancelFitnessPass()");
        Integer reservationNumber = new Integer(parameters[2]);
        Integer amount = new Integer(parameters[3]);
        return hotel.cancelFitnessPass(parameters[0],parameters[1],reservationNumber,amount);
    }

    private Integer getCasinoToken(String[] parameters)
            throws HotelCustomException
    {
        log.info("getCasinoToken()");
        Integer amount = new Integer(parameters[2]);
        return hotel.getCasinoToken(parameters[0],parameters[1],amount);
    }

    private boolean cancelCasinoToken(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("cancelCasinoToken()");
        Integer amount = new Integer(parameters[0]);
        return hotel.cancelCasinoToken(amount);
    }

    private MealOrder orderMeal(String[] parameters)
            throws HotelCustomException {
        log.info("orderMeal()");
        Integer reservationNumber = new Integer(parameters[2]);
        return hotel.orderMeal(parameters[0],parameters[1],reservationNumber);
    }

    private boolean cancelMeal(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("cancelMeal()");
        Integer reservationNumber = new Integer(parameters[2]);
        Integer orderNumber = new Integer(parameters[3]);
        return hotel.cancelMeal(parameters[0],parameters[1],reservationNumber,orderNumber);
    }

    private boolean orderExtraMiniBar(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("orderExtraMiniBar()");
        Integer reservationNumber = new Integer(parameters[2]);
        return hotel.orderExtraMiniBar(parameters[0],parameters[1],reservationNumber);
    }

    private boolean cancelExtraMiniBar(String[] parameters)
            throws HotelCustomException, NumberFormatException
    {
        log.info("cancelExtraMiniBar()");
        Integer reservationNumber = new Integer(parameters[2]);
        return hotel.orderExtraMiniBar(parameters[0],parameters[1],reservationNumber);
    }

    private Hotel getPortHotel()
    {
        // Connect to the Web Service
        URL wsdlLocation = null;
        try
        {
            wsdlLocation = new URL("http://localhost:8080/HotelImplService/HotelImpl?wsdl");
        }
        catch (MalformedURLException e)
        {
            e.printStackTrace();
        }
        QName serviceName = new QName("http://service.demo.ba.jboss.com/","HotelService");
        Service service = Service.create(wsdlLocation, serviceName);
        Hotel port =  service.getPort(Hotel.class);

        // Insert client handler
        BindingProvider bindingProvider = (BindingProvider)port;
        List<Handler> handlerChain = new ArrayList<Handler>();
        handlerChain.add(new JaxWSClientHeaderContextProcessor());
        bindingProvider.getBinding().setHandlerChain(handlerChain);

        return port;
    }

}


