/*
 * 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.
 */

#include "postgres.h"

#include "nodes/ag_extensible.h"
#include "nodes/nodes.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"

#include "optimizer/cypher_createplan.h"
#include "optimizer/cypher_pathnode.h"
#include "optimizer/pathnode.h"

const ExtensiblePathMethods cypher_create_path_methods = {
    CREATE_PATH_NAME, plan_cypher_create_path};
const ExtensiblePathMethods cypher_set_path_methods = {
    SET_PATH_NAME, plan_cypher_set_path};
const ExtensiblePathMethods cypher_delete_path_methods = {
    DELETE_PATH_NAME, plan_cypher_delete_path};
const ExtensiblePathMethods cypher_merge_path_methods = {
    MERGE_PATH_NAME, plan_cypher_merge_path};
const ExtensiblePathMethods cypher_vle_path_methods = {
    VLE_PATH_NAME, plan_cypher_vle_path};

ExtensiblePath *create_cypher_create_path(PlannerInfo *root, RelOptInfo *rel,
                                          List *custom_private)
{
    ExtensiblePath *cp;

    cp = makeNode(ExtensiblePath);

    cp->path.pathtype = T_ExtensiblePlan;

    cp->path.parent = rel;

    cp->path.param_info = NULL;

    // Do not allow parallel methods

    cp->path.rows = 0; // Basic CREATE will not return rows
    cp->path.startup_cost = 0; // Basic CREATE will not fetch any pages
    cp->path.total_cost = 0;

    // No output ordering for basic CREATE
    cp->path.pathkeys = NULL;

    // Disable all custom flags for now
    cp->flags = 0;

    cp->extensible_paths = rel->pathlist;
    cp->extensible_private = custom_private;
    cp->methods = &cypher_create_path_methods;

    return cp;
}

ExtensiblePath *create_cypher_set_path(PlannerInfo *root, RelOptInfo *rel,
                                   List *custom_private)
{
    ExtensiblePath *cp;

    cp = makeNode(ExtensiblePath);

    cp->path.pathtype = T_ExtensiblePlan;

    cp->path.parent = rel;

    cp->path.param_info = NULL;

    // Do not allow parallel methods

    cp->path.rows = 0; // Basic SET will not return rows
    cp->path.startup_cost = 0; // Basic SET will not fetch any pages
    cp->path.total_cost = 0;

    // No output ordering for basic SET
    cp->path.pathkeys = NULL;

    // Disable all custom flags for now
    cp->flags = 0;

    cp->extensible_paths = rel->pathlist;
    cp->extensible_private = custom_private;
    cp->methods = &cypher_set_path_methods;

    return cp;
}

/*
 * Creates a Delete Path. Makes the original path a child of the new
 * path. We leave it to the caller to replace the pathlist of the rel.
 */
ExtensiblePath *create_cypher_delete_path(PlannerInfo *root, RelOptInfo *rel,
                                          List *custom_private)
{
    ExtensiblePath *cp;

    cp = makeNode(ExtensiblePath);

    cp->path.pathtype = T_ExtensiblePlan;

    cp->path.parent = rel;

    cp->path.param_info = NULL;

    // Do not allow parallel methods

    cp->path.rows = 0;
    cp->path.startup_cost = 0;
    cp->path.total_cost = 0;

    // No output ordering for basic SET
    cp->path.pathkeys = NULL;

    // Disable all custom flags for now
    cp->flags = 0;

    // Make the original paths the children of the new path
    cp->extensible_paths = rel->pathlist;
    // Store the metadata Delete will need in the execution phase.
    cp->extensible_private = custom_private;
    // Tells Postgres how to turn this path to the correct CustomScan
    cp->methods = &cypher_delete_path_methods;

    return cp;
}

/*
 * Creates a Delete Path. Makes the original path a child of the new
 * path. We leave it to the caller to replace the pathlist of the rel.
 */
ExtensiblePath *create_cypher_merge_path(PlannerInfo *root, RelOptInfo *rel,
                                   List *custom_private)
{
    ExtensiblePath *cp;

    cp = makeNode(ExtensiblePath);

    cp->path.pathtype = T_ExtensiblePlan;

    cp->path.parent = rel;

    cp->path.param_info = NULL;

    // Do not allow parallel methods

    cp->path.rows = 0;
    cp->path.startup_cost = 0;
    cp->path.total_cost = 0;

    // No output ordering for basic SET
    cp->path.pathkeys = NULL;

    // Disable all custom flags for now
    cp->flags = 0;

    // Make the original paths the children of the new path
    cp->extensible_paths = rel->pathlist;
    // Store the metadata Delete will need in the execution phase.
    cp->extensible_private = custom_private;
    // Tells Postgres how to turn this path to the correct CustomScan
    cp->methods = &cypher_merge_path_methods;

    return cp;
}

ExtensiblePath *create_cypher_vle_path(PlannerInfo *root, RelOptInfo *rel,
                                         List *custom_private)
{
    ExtensiblePath *cp;

    cp = makeNode(ExtensiblePath);

    cp->path.pathtype = T_ExtensiblePlan;

    cp->path.parent = rel;

    cp->path.param_info = get_baserel_parampathinfo(root, rel, rel->lateral_relids);

    cp->path.pathtarget = rel->reltarget;

    // set a default for optimizer
    rel->rows = 10;
    cp->path.rows = 1000;
    cp->path.startup_cost = 0;
    cp->path.total_cost = 0;

    // No output ordering for basic SET
    cp->path.pathkeys = NULL;

    // Disable all custom flags for now
    cp->flags = 0;

    // Make the original paths the children of the new path
    cp->extensible_paths = rel->pathlist;
    // Store the metadata Delete will need in the execution phase.
    cp->extensible_private = custom_private;
    // Tells Postgres how to turn this path to the correct CustomScan
    cp->methods = &cypher_vle_path_methods;

    return cp;
}