/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.phoenix.hbase.index.master;

import java.io.IOException;
import java.util.List;

import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.master.AssignmentManager;
import org.apache.hadoop.hbase.master.LoadBalancer;
import org.apache.hadoop.hbase.master.RegionPlan;
import org.apache.hadoop.hbase.master.RegionStates;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer;
import org.apache.phoenix.util.MetaDataUtil;

/**
 * Defines of coprocessor hooks(to support secondary indexing) of operations on
 * {@link org.apache.hadoop.hbase.master.HMaster} process.
 */
public class IndexMasterObserver extends BaseMasterObserver {
    IndexLoadBalancer balancer = null;

    @Override
    public void preMasterInitialization(ObserverContext<MasterCoprocessorEnvironment> ctx)
            throws IOException {
        LoadBalancer loadBalancer =
                ctx.getEnvironment().getMasterServices().getAssignmentManager().getBalancer();
        if (loadBalancer instanceof IndexLoadBalancer) {
            balancer = (IndexLoadBalancer) loadBalancer;
        }
        super.preMasterInitialization(ctx);
    }

    @Override
    public void preCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
            HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
        TableName userTableName = null;
        if (balancer != null && desc.getValue(IndexLoadBalancer.PARENT_TABLE_KEY) != null) {
            userTableName =
                    TableName.valueOf(desc.getValue(IndexLoadBalancer.PARENT_TABLE_KEY));
            balancer.addTablesToColocate(userTableName, desc.getTableName());
        }
        if (userTableName != null) balancer.populateRegionLocations(userTableName);
        super.preCreateTableHandler(ctx, desc, regions);
    }

    @Override
    public void preModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
            TableName tableName, HTableDescriptor htd) throws IOException {
        HTableDescriptor oldDesc =
                ctx.getEnvironment().getMasterServices().getTableDescriptors().get(tableName);
        if (oldDesc.getValue(IndexLoadBalancer.PARENT_TABLE_KEY) == null
                && htd.getValue(IndexLoadBalancer.PARENT_TABLE_KEY) != null) {
            TableName userTableName =
                    TableName.valueOf(htd.getValue(IndexLoadBalancer.PARENT_TABLE_KEY));
            balancer.addTablesToColocate(userTableName, htd.getTableName());
        }
        super.preModifyTableHandler(ctx, tableName, htd);
    }

    @Override
    public void postMove(ObserverContext<MasterCoprocessorEnvironment> ctx, HRegionInfo region,
            ServerName srcServer, ServerName destServer) throws IOException {
        if (balancer != null && balancer.isTableColocated(region.getTable())) {
            AssignmentManager am = ctx.getEnvironment().getMasterServices().getAssignmentManager();
            RegionStates regionStates = am.getRegionStates();
            String tableName = region.getTable().getNameAsString();
            String correspondingTable =
                    region.getTable().getNameAsString()
                            .startsWith(MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX) ? MetaDataUtil
                            .getUserTableName(tableName) : MetaDataUtil
                            .getLocalIndexTableName(tableName);
            List<HRegionInfo> regions =
                    regionStates.getRegionsOfTable(TableName.valueOf(correspondingTable));
            for (HRegionInfo hri : regions) {
                if (Bytes.compareTo(region.getStartKey(), hri.getStartKey()) == 0
                        && destServer != null) {
                    balancer.regionOnline(hri, destServer);
                    am.addPlan(hri.getEncodedName(), new RegionPlan(hri, null, destServer));
                    am.unassign(hri);
                }
            }
        }
        super.postMove(ctx, region, srcServer, destServer);
    }

    @Override
    public void postDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> ctx,
            TableName tableName) throws IOException {
        if (balancer != null && balancer.isTableColocated(tableName)) {
            balancer.removeTablesFromColocation(tableName);
        }
    }
}