#!/usr/bin/env perl
# Copyright (c) 2021 OceanBase
# OceanBase Database Proxy(ODP) is licensed under Mulan PubL v2.
# You can use this software according to the terms and conditions of the Mulan PubL v2.
# You may obtain a copy of Mulan PubL v2 at:
#          http://license.coscl.org.cn/MulanPubL-2.0
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PubL v2 for more details.

use strict;
use warnings;
open my $fh, '<', "ob_errno.def";
my %map;
my $last_errno = 0;
my $error_count=0;
while(<$fh>) {
  my $error_msg;
  my $sqlstate;
  my $error_code;
  if (/^DEFINE_ERROR\(([^,]+),\s*([^,]*),\s*([^,]*),\s*([^,]*),\s*("[^"]*")/) {
    ++$error_count;
    #print "$1, $2, $3, $4, $5\n";
    $map{$1} = [$2, $3, $4, $5, $5];
    $last_errno = $2 if ($2 < $last_errno);
    $error_code = $2;
    $sqlstate = $4;
    $error_msg = $5;
  } elsif (/^DEFINE_ERROR_EXT\(([^,]+),\s*([^,]*),\s*([^,]*),\s*([^,]*),\s*([^,]*),\s*("[^"]*")/) {
    ++$error_count;
    #print "$1, $2, $3, $4, $5, $6\n";
    $map{$1} = [$2, $3, $4, $5, $6];
    $last_errno = $2 if ($2 < $last_errno);
    $error_code = $2;
    $sqlstate = $4;
    $error_msg = $5;
  }
  if (defined $error_code) {
    print "WARN: undefined SQLSTATE for $1\n" if ($sqlstate eq "\"\"");
    print "WARN: undefined error message for $1\n" if ($error_msg eq "\"\"");
    print "WARN: error code out of range: $1\n" if ($error_code <= -1 && $error_code > -3000);
  }
}

print "total error code: $error_count\n";
# check duplicate error number
my %dedup;
for my $oberr (keys %map) {
  my $errno = $map{$oberr}->[0];
  if (defined $dedup{$errno})
  {
    print "Error: error code($errno) is duplicated for $oberr and $dedup{$errno}\n";
    exit 1;
  } else {
    $dedup{$errno} = $oberr;
  }
}

# sort
my @pairs = map {[$_, $map{$_}->[0] ]} keys %map;
my @sorted = sort {$b->[1] <=> $a->[1]} @pairs;
my @errors = map {$_->[0]} @sorted;

# generate ob_errno.h
open my $fh_header, '>', "ob_errno.h";
print $fh_header '
// DO NOT EDIT. This file is automatically generated from `ob_errno.def\'.

// Copyright (c) 2021 OceanBase
// OceanBase Database Proxy(ODP) is licensed under Mulan PubL v2.
// You can use this software according to the terms and conditions of the Mulan PubL v2.
// You may obtain a copy of Mulan PubL v2 at:
//          http://license.coscl.org.cn/MulanPubL-2.0
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PubL v2 for more details.
// ob_errno.h

#ifndef OCEANBASE_LIB_OB_ERRNO_H_
#define OCEANBASE_LIB_OB_ERRNO_H_
#include "mysql_errno.h"
#include <stdint.h>
#
namespace oceanbase
{
namespace common
{
  static const int64_t OB_MAX_ERROR_CODE = 10000;
  ';
  print $fh_header "static const int OB_LAST_ERROR_CODE = $last_errno;";
  print $fh_header '
  static const int OB_ERR_SQL_START = -5000;
  static const int OB_ERR_SQL_END = -5999;
  ';
  for my $oberr (@errors) {
    print $fh_header "  static const int $oberr = $map{$oberr}->[0];\n";
  }
  print $fh_header "\n\n";
  for my $oberr (@errors) {
    print $fh_header "#define ${oberr}__USER_ERROR_MSG $map{$oberr}->[4]\n";
  }
  print $fh_header '

  const char* ob_strerror(int oberr);
  const char* ob_sqlstate(int oberr);
  const char* ob_str_user_error(int oberr);
  int ob_mysql_errno(int oberr);
} // end namespace common
} // end namespace oceanbase

#endif //OCEANBASE_LIB_OB_ERRNO_H_
';

# generate ob_errno.cpp
open my $fh_cpp, '>', "ob_errno.cpp";
print $fh_cpp '
// DO NOT EDIT. This file is automatically generated from `ob_errno.def\'.

// Copyright 2016 Alibaba Inc. All Rights Reserved.
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
// ob_errno.h

#include "ob_errno.h"
#include "ob_define.h"
using namespace oceanbase::common;

static const char* STR_ERROR[OB_MAX_ERROR_CODE];
static const char* SQLSTATE[OB_MAX_ERROR_CODE];
static const char* STR_USER_ERROR[OB_MAX_ERROR_CODE];
static int MYSQL_ERRNO[OB_MAX_ERROR_CODE];

static struct ObStrErrorInit
{
  ObStrErrorInit()
  {
    memset(STR_ERROR, 0, sizeof(STR_ERROR));
    memset(SQLSTATE, 0, sizeof(SQLSTATE));
    memset(MYSQL_ERRNO, 0, sizeof(MYSQL_ERRNO));
    memset(STR_USER_ERROR, 0, sizeof(STR_USER_ERROR));
    ';

    for my $oberr (@errors) {
      if (0 > $map{$oberr}->[0]) {
	print $fh_cpp "    MYSQL_ERRNO[-$oberr] = $map{$oberr}->[1];\n";
	print $fh_cpp "    SQLSTATE[-$oberr] = $map{$oberr}->[2];\n";
	print $fh_cpp "    STR_ERROR[-$oberr] = $map{$oberr}->[3];\n";
	print $fh_cpp "    STR_USER_ERROR[-$oberr] = $map{$oberr}->[4];\n";
    }
  }

  print $fh_cpp '
  }
} local_init;

namespace oceanbase
{
namespace common
{
  const char* ob_strerror(int err)
  {
    const char* ret = "Unknown error";
    if (OB_LIKELY(0 >= err && err > -OB_MAX_ERROR_CODE)) {
      ret = STR_ERROR[-err];
      if (OB_UNLIKELY(NULL == ret || \'\0\' == ret[0]))
      {
        ret = "Unknown Error";
      }
    }
    return ret;
  }
  const char* ob_str_user_error(int err)
  {
    const char* ret = NULL;
    if (OB_LIKELY(0 >= err && err > -OB_MAX_ERROR_CODE)) {
      ret = STR_USER_ERROR[-err];
      if (OB_UNLIKELY(NULL == ret || \'\0\' == ret[0])) {
        ret = NULL;
      }
    }
    return ret;
  }
  const char* ob_sqlstate(int err)
  {
    const char* ret = "HY000";
    if (OB_LIKELY(0 >= err && err > -OB_MAX_ERROR_CODE)) {
      ret = SQLSTATE[-err];
      if (OB_UNLIKELY(NULL == ret || \'\0\' == ret[0])) {
        ret = "HY000";
      }
    }
    return ret;
  }
  int ob_mysql_errno(int err)
  {
    int ret = -1;
    if (OB_LIKELY(0 >= err && err > -OB_MAX_ERROR_CODE)) {
      ret = MYSQL_ERRNO[-err];
    }
  return ret;
  }
} // end namespace common
} // end namespace oceanbase
  ';
