/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see .
 */
package org.alfresco.repo.clt;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.remote.ClientTicketHolder;
import org.alfresco.service.cmr.avmsync.AVMSyncService;
import org.alfresco.service.cmr.remote.AVMRemote;
import org.alfresco.service.cmr.remote.RepoRemote;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.util.Pair;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * This is the base class for AVM clts.
 * @author britt
 */
public abstract class CltBase
{
    /**
     * The instance of the remote interface.
     */
    protected AVMRemote fAVMRemote;
    /**
     * The instance of the remote sync service interface.
     */
    protected AVMSyncService fAVMSyncService;
    /**
     * The instance of the remote repo interface.
     */
    protected RepoRemote fRepoRemote;
    /**
     * The ApplicationContext.
     */
    protected ConfigurableApplicationContext fContext;
    /**
     * The Authentication Service.
     */
    protected AuthenticationService fAuthenticationService;
    /**
     * The usage string.
     */
    private String fUsage;
    /**
     * Construct a new one. This takes care of instantiating
     * the application context and grabs references to the
     * services.
     * @param args The program arguments.
     */
    protected CltBase()
    {
        fContext = new ClassPathXmlApplicationContext("clt-context.xml");
        fAVMRemote = (AVMRemote)fContext.getBean("avmRemote");
        fAVMSyncService = (AVMSyncService)fContext.getBean("avmSyncService");
        fRepoRemote = (RepoRemote)fContext.getBean("repoRemote");
        fAuthenticationService = (AuthenticationService)fContext.getBean("authenticationService");
        fAuthenticationService.authenticate(System.getenv("ALFUSER"), System.getenv("ALFPASSWORD").toCharArray());
        String ticket = fAuthenticationService.getCurrentTicket();
        ((ClientTicketHolder)fContext.getBean("clientTicketHolder")).setTicket(ticket);
    }
    /**
     * All clts go through this call. This parses the arguments, exits if
     * there are any errors and then passes the broken flags and arguments
     * to the run method of the derived clt.
     * @param args The raw command line arguments.
     * @param flagDefs The definition of what flags to accept and their
     * arities.
     * @param minArgs The minimum number of actual arguments expected.
     * @param usageMessage The message that should be printed if there is a
     * syntax error.
     */
    public void exec(String [] args,
                     Object [] flagDefs,
                     int minArgs,
                     String usageMessage)
    {
        fUsage = usageMessage;
        Map flagArgs = new HashMap();
        Map> flagValues = new HashMap>();
        List actualArgs = new ArrayList();
        // Convert the flag definitions into a convenient form.
        for (int i = 0; i < flagDefs.length / 2; i++)
        {
            flagArgs.put((String)flagDefs[i * 2], (Integer)flagDefs[i * 2 + 1]);
        }
        // Walk through the raw command line arguments.
        int pos = 0;
        while (pos < args.length)
        {
            if (args[pos].equals("-h"))
            {
                usage();
            }
            // If the argument is one of the accepted flags then it's
            // a flag.
            if (flagArgs.containsKey(args[pos]))
            {
                String flag = args[pos];
                pos++;
                int count = flagArgs.get(flag);
                // Check for too few arguments
                if (args.length - pos < count)
                {
                    usage();
                }
                // Stuff the parsed flag away.
                List flArgs = new ArrayList();
                for (int i = 0; i < count; i++)
                {
                    flArgs.add(args[pos + i]);
                }
                flagValues.put(flag, flArgs);
                pos += count;
                continue;
            }
            // Otherwise its just a plain old arg.
            actualArgs.add(args[pos]);
            pos++;
        }
        // Check for too few arguments.
        if (actualArgs.size() < minArgs)
        {
            usage();
        }
        // Do the work.
        run(flagValues, actualArgs);
        // Cleanup.
        fContext.close();
    }
    /**
     * Handle syntax error by exiting.
     */
    protected void usage()
    {
        System.err.println(fUsage);
        fContext.close();
        System.exit(1);
    }
    /**
     * Utility to split an AVM path into a parent path and a
     * base name.
     * @param path The path to split.
     * @return An array of 1 or 2 Strings representing the parent path
     * and the base name, or just the path if the path given is a root path.
     */
    protected String[] splitPath(String path)
    {
        if (path.endsWith(":/"))
        {
            String [] ret = { path };
            return ret;
        }
        int lastSlash = path.lastIndexOf("/");
        if (lastSlash == -1)
        {
            System.err.println("Malformed path: " + path);
            fContext.close();
            System.exit(1);
        }
        String name = path.substring(lastSlash + 1);
        String parent = path.substring(0, lastSlash);
        if (parent.endsWith(":"))
        {
            parent = parent + "/";
        }
        while (parent.endsWith("/") && !parent.endsWith(":/"))
        {
            parent = parent.substring(0, parent.length() - 1);
        }
        String [] ret = { parent, name };
        return ret;
    }
    protected void copyStream(InputStream in, OutputStream out)
    {
        try
        {
            byte [] buff = new byte[8192];
            int read = 0;
            while ((read = in.read(buff)) != -1)
            {
                out.write(buff, 0, read);
            }
            in.close();
            out.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
            fContext.close();
            System.exit(1);
        }
    }
    protected Pair splitPathVersion(String pathVersion)
    {
        int index = pathVersion.lastIndexOf('@');
        if (index == -1)
        {
            usage();
        }
        String path = pathVersion.substring(0, index);
        int version = Integer.parseInt(pathVersion.substring(index + 1));
        return new Pair(path, version);
    }
    protected abstract void run(Map> flags, List args);
}