/*
 * Copyright 2015 MovingBlocks
 *
 * 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.terasology.module;

import com.google.common.base.Preconditions;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.naming.Name;
import org.terasology.naming.Version;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Implementation of ModuleRegistry based around a com.google.common.collect.Table.
 *
 * @author Immortius
 */
public class TableModuleRegistry implements ModuleRegistry {

    private static final Logger LOG = LoggerFactory.getLogger(TableModuleRegistry.class);

    private final Table<Name, Version, Module> modules = HashBasedTable.create();
    private final Map<Name, Module> latestModules = Maps.newHashMap();

    @Override
    public boolean add(Module module) {
        Preconditions.checkNotNull(module);
        if (!modules.contains(module.getId(), module.getVersion())) {
            modules.put(module.getId(), module.getVersion(), module);
            Module previousLatest = latestModules.get(module.getId());
            if (previousLatest == null || previousLatest.getVersion().compareTo(module.getVersion()) <= 0) {
                latestModules.put(module.getId(), module);
            }
            return true;
        } else {
            LOG.error("Module {}-{} already registered from {}, cannot register same module from {}",
                    module.getId(),
                    module.getVersion(),
                    modules.get(module.getId(), module.getVersion()).getLocations(),
                    module.getLocations());
        }
        return false;
    }

    @Override
    public boolean remove(Object o) {
        if (o instanceof Module) {
            Module module = (Module) o;
            if (modules.remove(module.getId(), module.getVersion()) != null) {
                Module latest = latestModules.get(module.getId());
                if (latest.getVersion().compareTo(module.getVersion()) == 0) {
                    updateLatestFor(module.getId());
                }

                return true;
            }
            return false;
        }
        return false;
    }

    private void updateLatestFor(Name moduleId) {
        Module newLatest = null;
        for (Module remainingModule : modules.row(moduleId).values()) {
            if (newLatest == null || remainingModule.getVersion().compareTo(newLatest.getVersion()) > 0) {
                newLatest = remainingModule;
            }
        }
        if (newLatest != null) {
            latestModules.put(moduleId, newLatest);
        } else {
            latestModules.remove(moduleId);
        }
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean result = false;
        for (Object o : c) {
            result |= remove(o);
        }
        return result;
    }

    @Override
    public boolean addAll(Collection<? extends Module> c) {
        boolean result = false;
        for (Object o : c) {
            if (o instanceof Module) {
                result |= add((Module) o);
            }
        }
        return result;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        Set<Module> modulesToRetain = Sets.newHashSet();
        for (Object o : c) {
            if (o instanceof Module) {
                modulesToRetain.add((Module) o);
            }
        }

        boolean changed = false;
        Iterator<Module> moduleIterator = modules.values().iterator();
        while (moduleIterator.hasNext()) {
            Module next = moduleIterator.next();
            if (!modulesToRetain.contains(next)) {
                moduleIterator.remove();
                changed = true;
            }
        }

        if (changed) {
            for (Name name : modules.rowKeySet()) {
                updateLatestFor(name);
            }
        }
        return changed;
    }

    @Override
    public Set<Name> getModuleIds() {
        return Sets.newLinkedHashSet(modules.rowKeySet());
    }

    @Override
    public Collection<Module> getModuleVersions(Name id) {
        return Collections.unmodifiableCollection(modules.row(id).values());
    }

    @Override
    public Module getLatestModuleVersion(Name id) {
        return latestModules.get(id);
    }

    @Override
    public Module getLatestModuleVersion(Name id, Version minVersion, Version maxVersion) {
        Module module = latestModules.get(id);
        if (module != null) {
            if (module.getVersion().compareTo(maxVersion) < 0 && module.getVersion().compareTo(minVersion) >= 0) {
                return module;
            }
            if (module.getVersion().compareTo(minVersion) >= 0) {
                Module result = null;
                for (Map.Entry<Version, Module> item : modules.row(id).entrySet()) {
                    if (item.getKey().compareTo(minVersion) >= 0 && item.getKey().compareTo(maxVersion) < 0
                            && (result == null || item.getKey().compareTo(result.getVersion()) > 0)) {
                        result = item.getValue();
                    }
                }
                return result;
            }
        }
        return null;
    }

    @Override
    public Module getModule(Name moduleId, Version version) {
        return modules.get(moduleId, version);
    }

    @Override
    public int size() {
        return modules.size();
    }

    @Override
    public boolean isEmpty() {
        return modules.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        if (o instanceof Module) {
            Module module = (Module) o;
            return modules.contains(module.getId(), module.getVersion());
        }
        return false;
    }

    @Override
    public Iterator<Module> iterator() {
        Iterator<Module> it = modules.values().iterator();
        return new Iterator<Module>() {

            private Module current;

            @Override
            public boolean hasNext() {
                return it.hasNext();
            }

            @Override
            public Module next() {
                current = it.next();
                return current;
            }

            @Override
            public void remove() {
                it.remove();
                Module latest = latestModules.get(current.getId());
                if (latest.getVersion().compareTo(current.getVersion()) == 0) {
                    updateLatestFor(current.getId());
                }
            }
        };
    }

    @Override
    public Object[] toArray() {
        return modules.values().toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return modules.values().toArray(a);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        for (Object o : c) {
            if (!contains(o)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void clear() {
        modules.clear();
        latestModules.clear();
    }
}

@@ -1307,19 +1307,22 @@
  Logger 
-LOG
+logger
  = Logge
@@ -2072,11 +2072,14 @@
     
-LOG
+logger
 .err
@@ -5700,20 +5700,16 @@
 on) %3C 0%0A
-    
         

rename logger