/**
 * Copyright (C) 2009-2012 enStratus Networks Inc
 *
 * ====================================================================
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ====================================================================
 */

package org.dasein.cloud.joyent;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

import org.apache.log4j.Logger;
import org.dasein.cloud.AbstractCloud;
import org.dasein.cloud.CloudErrorType;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.joyent.compute.JoyentComputeServices;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class SmartDataCenter extends AbstractCloud {
    static private @Nonnull String getLastItem(@Nonnull String name) {
        int idx = name.lastIndexOf('.');
        
        if( idx < 0 ) {
            return name;
        }
        else if( idx == (name.length()-1) ) {
            return "";
        }
        return name.substring(idx+1);
    }
    
    static public @Nonnull Logger getLogger(@Nonnull Class<?> cls, @Nonnull String type) {
        String pkg = getLastItem(cls.getPackage().getName());
        
        if( pkg.equals("joyent") ) {
            pkg = "";
        }
        else {
            pkg = pkg + ".";
        }
        return Logger.getLogger("dasein.cloud.joyent." + type + "." + pkg + getLastItem(cls.getName()));
    }
    
    public SmartDataCenter() { }
    
    @Override
    public @Nonnull JoyentComputeServices getComputeServices() {
        return new JoyentComputeServices(this);
    }
    
    @Override
    public @Nonnull String getCloudName() {
        return "Joyent Cloud";
    }
    
    @Override
    public @Nonnull JoyentDataCenter getDataCenterServices() {
        return new JoyentDataCenter(this);
    }
    
    static private final HashMap<String,Map<String,String>> endpointCache = new HashMap<String,Map<String,String>>();
    
    public @Nonnull String getEndpoint() throws CloudException, InternalException {
        ProviderContext ctx = getContext();

        if( ctx == null ) {
            throw new CloudException("No context has been established for this request");
        }
        String e = ctx.getEndpoint();
        
        if( e == null ) { 
            e = "https://us-west-1.api.joyentcloud.com";
        }
        String[] parts = e.split(",");
        
        if( parts == null || parts.length < 1 ) {
            parts = new String[] { e };
        }
        String r = ctx.getRegionId();
        
        if( r == null ) {
            return parts[0];
        }
        if( endpointCache.containsKey(e) ) {
            Map<String,String> cache = endpointCache.get(e);
            
            if( cache != null && cache.containsKey(r) ) {
                String endpoint = cache.get(r);
                
                if( endpoint != null ) {
                    return endpoint;
                }
            }
        }
        JoyentMethod method = new JoyentMethod(this);

        String json = method.doGetJson(parts[0], "datacenters");
        try {
            JSONObject ob = new JSONObject(json);
            JSONArray ids = ob.names();
            
            for( int i=0; i<ids.length(); i++ ) {
                String regionId = ids.getString(i);
                
                if( regionId.equals(r) && ob.has(regionId) ) {
                    String endpoint = ob.getString(regionId);
                    Map<String,String> cache;
                    
                    if( endpointCache.containsKey(e) ) {
                        cache = endpointCache.get(e);
                    }
                    else {
                        cache = new HashMap<String,String>();
                        endpointCache.put(e, cache);
                    }
                    cache.put(r, endpoint);
                    return endpoint;
                }
            }
            throw new CloudException("No endpoint exists for " + r);
        }
        catch( JSONException ex ) {
            throw new CloudException(ex);
        }        
    }
    
    @Override
    public @Nonnull String getProviderName() {
        return "Joyent";
    }
    
    public @Nonnegative long parseTimestamp(String time) throws CloudException {
        if( time == null ) {
            return 0L;
        }
        int idx = time.lastIndexOf('+');
        
        time = time.substring(0,idx);
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
            
        fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
        if( time.length() > 0 ) {
            try {
                return fmt.parse(time).getTime();
            } 
            catch( ParseException e ) {
                throw new CloudException("Could not parse date: " + time);
            }
        }
        return 0L;
    }
    
    @Override
    public @Nullable String testContext() {
        Logger logger = getLogger(SmartDataCenter.class, "std");
        
        if( logger.isTraceEnabled() ) {
            logger.trace("enter - " + SmartDataCenter.class.getName() + ".textContext()");
        }
        try {
            try {
                ProviderContext ctx = getContext();

                if( ctx == null ) {
                    return null;
                }
                String pk = new String(ctx.getAccessPublic(), "utf-8");

                JoyentMethod method = new JoyentMethod(this);
                
                try {
                    method.doGetJson(getEndpoint(), "datacenters");
                    return pk;
                }
                catch( CloudException e ) {
                    if( e.getErrorType().equals(CloudErrorType.AUTHENTICATION) ) {
                        return null;
                    }
                    logger.warn("Cloud error testing Joyent context: " + e.getMessage());
                    if( logger.isTraceEnabled() ) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
            catch( Throwable t ) {
                logger.warn("Failed to test Joyent connection context: " + t.getMessage());
                if( logger.isTraceEnabled() ) {
                    t.printStackTrace();
                }
                return null;
            }
        }
        finally {
            if( logger.isTraceEnabled() ) {
                logger.trace("exit - " + SmartDataCenter.class.getName() + ".testContext()");
            }
        }
    }
}

@@ -5105,32 +5105,62 @@
 n 0L;%0A        %7D%0A
+        SimpleDateFormat fmt;%0A
         int idx 
@@ -5184,33 +5184,213 @@
 f('+');%0A        
-%0A
+if (idx %3C 0) %7B%0A            idx = time.lastIndexOf('Z');%0A            if (idx %3C 0) %7B%0A                throw new CloudException(%22Could not parse timestamp: %22 + time);%0A            %7D%0A    
         time = t
@@ -5411,32 +5411,46 @@
 0,idx);%0A        
+    fmt = new 
 SimpleDateFormat
@@ -5445,24 +5445,125 @@
 leDateFormat
+(%22yyyy-MM-dd'T'HH:mm:ss.SSS%22);%0A        %7D else %7B%0A            time = time.substring(0,idx);%0A           
  fmt = new S
@@ -5604,36 +5604,34 @@
 m:ss%22);%0A        
-    
+%7D%0A
 %0A        fmt.set
@@ -5888,12 +5888,17 @@
 rse 
-date
+timestamp
 : %22 

Parse other form of ISO 8601 timestamps