#include "expression.h"
#include "evaluation.h"

expression::expression(
    int expr_id,
    const char *op_name,
    const char *op_type,
    int *inputs,
    int num_inputs)
    : expr_id_(expr_id), op_name_(op_name), op_type_(op_type)
{
    // initialize inputs_ with the given inputs from our expression.
    for (int i = 0; i < num_inputs; ++i)
    {
        inputs_.push_back(inputs[i]);
    }

}

// getter methods
int expression::get_expr_id() const
{
    return expr_id_;
}

const std::string& expression::get_op_name() const
{
    return op_name_;
}

const std::string& expression::get_op_type() const
{
    return op_type_;
}

const std::vector<int>& expression::get_inputs() const
{
    return inputs_;
}

tensor expression::get_op_param_tensor(const std::string& key) const {
        auto it = op_params_tensor_.find(key);
        if (it != op_params_tensor_.end()) {
            return it->second;
        } else {
            throw std::out_of_range("No operation parameter tensor found for key: " + key);
        }
}

double expression::get_op_param_double(const std::string& key) const {
    auto it = op_params_double_.find(key);
    if (it != op_params_double_.end()) {
        return it->second;
    } else {
        return 0.0;
    }
}

int expression::get_op_param_int(const std::string& key) const {
    auto it = op_params_int_.find(key);
    if (it != op_params_int_.end()) {
        return it->second;
    } else {
        return 0;
    }
}

void expression::add_op_param_double(
    const char *key,
    double value)
{

    std::cout << "Adding operation parameter (double) with key: " << key << std::endl;

    const char *w = "weight"; 
    const char *b = "bias"; 
    const char *v = "value"; // for backwards compatibility with p3 tests
    const char *k = "kernel_size";
    const char *s = "stride";
    const char *i = "in_channels";
    const char *o = "out_channels";
    const char *p = "padding";

    if((*key) == (*w)|| (*key) == (*b) || (*key) == (*v)){ // handle weight, bias, value tensor values
        op_params_tensor_[key] = tensor(value);
    } else if ((*key) == (*k) || (*key) == (*s) || (*key) == (*i) || (*key) == (*o)) { // handle kernel_size, stride, in_channels, out_channels int values
        op_params_int_[key] = int(value);
    } else if ((*key) == (*p)){ // handle padding double value
        op_params_double_[key] = value;
    }else{
        op_params_tensor_[key] = tensor(value);
    }
}

void expression::add_op_param_ndarray(
    const char *key,
    int dim,
    size_t shape[],
    double data[])
{
    std::cout << "Adding operation parameter (ndarray) with key: " << key << std::endl;

    const char *w = "weight"; 
    const char *b = "bias"; 

  if((*key) == (*w)|| (*key) == (*b) ){
    op_params_tensor_[key] = tensor(dim, shape, data);
  }else{
    op_params_tensor_[key] = tensor(dim, shape, data);
  }

}
