/**
 *
 */
package com.thinkbiganalytics.metadata.rest;

/*-
 * #%L
 * thinkbig-metadata-rest-controller
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * 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.
 * #L%
 */

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.thinkbiganalytics.metadata.api.category.Category;
import com.thinkbiganalytics.metadata.api.datasource.DerivedDatasource;
import com.thinkbiganalytics.metadata.api.feed.Feed.State;
import com.thinkbiganalytics.metadata.rest.model.data.Datasource;
import com.thinkbiganalytics.metadata.rest.model.data.DatasourceDefinition;
import com.thinkbiganalytics.metadata.rest.model.data.DirectoryDatasource;
import com.thinkbiganalytics.metadata.rest.model.data.HiveTableDatasource;
import com.thinkbiganalytics.metadata.rest.model.feed.Feed;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedCategory;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedDestination;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedPrecondition;
import com.thinkbiganalytics.metadata.rest.model.feed.FeedSource;
import com.thinkbiganalytics.metadata.rest.model.feed.InitializationStatus;
import com.thinkbiganalytics.metadata.rest.model.op.FeedOperation;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

/**
 * Convenience functions and methods to transform between the metadata domain model and the REST model.
 */
public class Model {

    public static final Function<com.thinkbiganalytics.metadata.api.feed.InitializationStatus, InitializationStatus> DOMAIN_TO_INIT_STATUS
        = new Function<com.thinkbiganalytics.metadata.api.feed.InitializationStatus, InitializationStatus>() {
        @Override
        public InitializationStatus apply(com.thinkbiganalytics.metadata.api.feed.InitializationStatus domain) {
            InitializationStatus status = new InitializationStatus();
            status.setState(InitializationStatus.State.valueOf(domain.getState().name()));
            status.setTimestamp(domain.getTimestamp());
            return status;
        }
    };

    // @formatter:off
    public static final Function<com.thinkbiganalytics.metadata.api.feed.FeedPrecondition, FeedPrecondition> DOMAIN_TO_FEED_PRECOND
        = new Function<com.thinkbiganalytics.metadata.api.feed.FeedPrecondition, FeedPrecondition>() {
        @Override
        public FeedPrecondition apply(com.thinkbiganalytics.metadata.api.feed.FeedPrecondition domain) {
            FeedPrecondition precond = new FeedPrecondition();
            return precond;
        }
    };
    /**
     * Convert a Domin DatasourceDefinition to the Rest Model
     */
    public static final Function<com.thinkbiganalytics.metadata.api.datasource.DatasourceDefinition, DatasourceDefinition> DOMAIN_TO_DS_DEFINITION
        = new Function<com.thinkbiganalytics.metadata.api.datasource.DatasourceDefinition, DatasourceDefinition>() {
        @Override
        public DatasourceDefinition apply(com.thinkbiganalytics.metadata.api.datasource.DatasourceDefinition domain) {
            DatasourceDefinition dsDef = new DatasourceDefinition();
            dsDef.setDatasourceType(domain.getDatasourceType());
            dsDef.setProcessorType(domain.getProcessorType());
            if (domain.getConnectionType() != null) {
                dsDef.setConnectionType(DatasourceDefinition.ConnectionType.valueOf(domain.getConnectionType().name()));
            }
            dsDef.setIdentityString(domain.getIdentityString());
            dsDef.setDatasourcePropertyKeys(domain.getDatasourcePropertyKeys());
            dsDef.setTitle(domain.getTitle());
            dsDef.setDescription(domain.getDescription());
            return dsDef;
        }
    };
    public static final Function<DerivedDatasource, com.thinkbiganalytics.metadata.rest.model.data.DerivedDatasource> DOMAIN_TO_DERIVED_DS
        = new Function<DerivedDatasource, com.thinkbiganalytics.metadata.rest.model.data.DerivedDatasource>() {
        @Override
        public com.thinkbiganalytics.metadata.rest.model.data.DerivedDatasource apply(DerivedDatasource domain) {
            com.thinkbiganalytics.metadata.rest.model.data.DerivedDatasource ds = new com.thinkbiganalytics.metadata.rest.model.data.DerivedDatasource();
            ds.setId(domain.getId().toString());
            ds.setName(domain.getName());
            ds.setDescription(domain.getDescription());
            ds.setProperties(domain.getProperties());
            ds.setDatasourceType(domain.getDatasourceType());
            return ds;
        }
    };
    public static final Function<com.thinkbiganalytics.metadata.api.datasource.Datasource, Datasource> DOMAIN_TO_DS = DOMAIN_TO_DS(true);
    public static final Function<com.thinkbiganalytics.metadata.api.feed.FeedSource, FeedSource> DOMAIN_TO_FEED_SOURCE
        = new Function<com.thinkbiganalytics.metadata.api.feed.FeedSource, FeedSource>() {
        @Override
        public FeedSource apply(com.thinkbiganalytics.metadata.api.feed.FeedSource domain) {
            FeedSource src = new FeedSource();
            src.setDatasource(DOMAIN_TO_DS.apply(domain.getDatasource()));
            return src;
        }
    };
    public static final Function<com.thinkbiganalytics.metadata.api.feed.FeedDestination, FeedDestination> DOMAIN_TO_FEED_DESTINATION
        = new Function<com.thinkbiganalytics.metadata.api.feed.FeedDestination, FeedDestination>() {
        @Override
        public FeedDestination apply(com.thinkbiganalytics.metadata.api.feed.FeedDestination domain) {
            FeedDestination dest = new FeedDestination();
            dest.setDatasource(DOMAIN_TO_DS.apply(domain.getDatasource()));
            return dest;
        }
    };
    public static final Function<com.thinkbiganalytics.metadata.api.op.FeedOperation, FeedOperation> DOMAIN_TO_FEED_OP
        = new Function<com.thinkbiganalytics.metadata.api.op.FeedOperation, FeedOperation>() {
        @Override
        public FeedOperation apply(com.thinkbiganalytics.metadata.api.op.FeedOperation domain) {
            FeedOperation op = new FeedOperation();
            op.setOperationId(domain.getId().toString());
            op.setStartTime(domain.getStartTime());
            op.setStopTime(domain.getStopTime());
            op.setState(FeedOperation.State.valueOf(domain.getState().name()));
            op.setStatus(domain.getStatus());
            op.setResults(domain.getResults().entrySet().stream()
                              .collect(Collectors.toMap(Map.Entry::getKey,
                                                        e -> e.getValue().toString())));

            return op;
        }
    };
    public static final Function<Category, FeedCategory> DOMAIN_TO_FEED_CATEGORY = new Function<Category, FeedCategory>() {
        @Override
        public FeedCategory apply(Category category) {
            FeedCategory feedCategory = new FeedCategory();
            feedCategory.setId(category.getId().toString());
            feedCategory.setSystemName(category.getName());
            feedCategory.setDisplayName(category.getDisplayName());
            feedCategory.setDescription(category.getDescription());
            return feedCategory;
        }
    };
    public static final Function<com.thinkbiganalytics.metadata.api.feed.Feed, Feed> DOMAIN_TO_FEED = DOMAIN_TO_FEED(true);

    private Model() {
    }

    public static final Function<com.thinkbiganalytics.metadata.api.feed.Feed, Feed> DOMAIN_TO_FEED(boolean addSources) {
        return new Function<com.thinkbiganalytics.metadata.api.feed.Feed, Feed>() {
            @Override
            public Feed apply(com.thinkbiganalytics.metadata.api.feed.Feed domain) {
                Feed feed = new Feed();
                feed.setId(domain.getId().toString());
                feed.setSystemName(domain.getName());
                feed.setDisplayName(domain.getDisplayName());
                feed.setDescription(domain.getDescription());
                feed.setState(Feed.State.valueOf(domain.getState().name()));
                feed.setCreatedTime(domain.getCreatedTime());
                feed.setCurrentInitStatus(DOMAIN_TO_INIT_STATUS.apply(domain.getCurrentInitStatus()));
                if (domain.getCategory() != null) {
                    feed.setCategory(DOMAIN_TO_FEED_CATEGORY.apply(domain.getCategory()));
                }

                if (addSources) {
                    @SuppressWarnings("unchecked")
                    Collection<FeedSource> sources = Collections2.transform(domain.getSources(), DOMAIN_TO_FEED_SOURCE);
                    feed.setSources(new HashSet<FeedSource>(sources));

                    @SuppressWarnings("unchecked")
                    Collection<FeedDestination> destinations = Collections2.transform(domain.getDestinations(), DOMAIN_TO_FEED_DESTINATION);
                    feed.setDestinations(new HashSet<FeedDestination>(destinations));
                }

                for (Entry<String, Object> entry : domain.getProperties().entrySet()) {
                    if (entry.getValue() != null) {
                        feed.getProperties().setProperty(entry.getKey(), entry.getValue().toString());
                    }
                }

                return feed;
            }
        };
    }


    public static final Function<com.thinkbiganalytics.metadata.api.datasource.Datasource, Datasource> DOMAIN_TO_DS(boolean addConnections) {
        return new Function<com.thinkbiganalytics.metadata.api.datasource.Datasource, Datasource>() {
            @Override
            public Datasource apply(com.thinkbiganalytics.metadata.api.datasource.Datasource domain) {
                Datasource ds;
                if (domain instanceof DerivedDatasource) {
                    ds = DOMAIN_TO_DERIVED_DS.apply((DerivedDatasource) domain);
                } else {
                    ds = new Datasource();
                    ds.setId(domain.getId().toString());
                    ds.setName(domain.getName());
                    ds.setDescription(domain.getDescription());
                }
                if (addConnections) {
                    addConnections(domain, ds);
                }
                return ds;
            }
        };
    }

    protected static void addConnections(com.thinkbiganalytics.metadata.api.datasource.Datasource domain, Datasource datasource) {
        for (com.thinkbiganalytics.metadata.api.feed.FeedSource domainSrc : domain.getFeedSources()) {
            Feed feed = new Feed();
            feed.setId(domainSrc.getFeed().getId().toString());
            feed.setSystemName(domainSrc.getFeed().getName());

            datasource.getSourceForFeeds().add(feed);
        }
        for (com.thinkbiganalytics.metadata.api.feed.FeedDestination domainDest : domain.getFeedDestinations()) {
            Feed feed = new Feed();
            feed.setId(domainDest.getFeed().getId().toString());
            feed.setSystemName(domainDest.getFeed().getName());

            datasource.getDestinationForFeeds().add(feed);
        }
    }


    public static com.thinkbiganalytics.metadata.api.feed.Feed updateDomain(Feed feed, com.thinkbiganalytics.metadata.api.feed.Feed domain) {
        domain.setDisplayName(feed.getDisplayName());
        domain.setDescription(feed.getDescription());
        domain.setState(State.valueOf(feed.getState().name()));
        return domain;
    }

    public static void validateCreate(Feed feed) {
        // TODO Auto-generated method stub

    }


    public static void validateCreate(String fid, FeedDestination dest) {
        // TODO Auto-generated method stub

    }


    public static void validateCreate(HiveTableDatasource ds) {
        // TODO Auto-generated method stub

    }


    public static void validateCreate(DirectoryDatasource ds) {
        // TODO Auto-generated method stub

    }

    // @formatter:on

}
