# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
#
# 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.

import errno
import os
import numpy as np
import paddle

from .utils.io import Checkpoint
from . import backbones
from .static_model import StaticModel


def export_onnx(path_prefix, feed_vars, fetch_vars, executor, program):

    from paddle2onnx.graph import PaddleGraph, ONNXGraph
    from paddle2onnx.passes import PassManager

    opset_version = 10
    enable_onnx_checker = True
    verbose = False

    paddle_graph = PaddleGraph.build_from_program(program, feed_vars,
                                                  fetch_vars,
                                                  paddle.fluid.global_scope())

    onnx_graph = ONNXGraph.build(paddle_graph, opset_version, verbose)
    onnx_graph = PassManager.run_pass(onnx_graph, ['inplace_node_pass'])

    onnx_proto = onnx_graph.export_proto(enable_onnx_checker)

    try:
        # mkdir may conflict if pserver and trainer are running on the same machine
        dirname = os.path.dirname(path_prefix)
        os.makedirs(dirname)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise
    model_path = path_prefix + ".onnx"
    if os.path.isdir(model_path):
        raise ValueError("'{}' is an existing directory.".format(model_path))

    with open(model_path, 'wb') as f:
        f.write(onnx_proto.SerializeToString())


def export(args):
    checkpoint = Checkpoint(
        rank=0,
        world_size=1,
        embedding_size=args.embedding_size,
        num_classes=None,
        checkpoint_dir=args.checkpoint_dir, )

    test_program = paddle.static.Program()
    startup_program = paddle.static.Program()

    test_model = StaticModel(
        main_program=test_program,
        startup_program=startup_program,
        backbone_class_name=args.backbone,
        embedding_size=args.embedding_size,
        mode='test', )

    gpu_id = int(os.getenv("FLAGS_selected_gpus", 0))
    place = paddle.CUDAPlace(gpu_id)
    exe = paddle.static.Executor(place)
    exe.run(startup_program)

    checkpoint.load(program=test_program, for_train=False, dtype='float32')
    print("Load checkpoint from '{}'.".format(args.checkpoint_dir))

    path = os.path.join(args.output_dir, args.backbone)
    if args.export_type == 'onnx':
        feed_vars = [test_model.backbone.input_dict['image'].name]
        fetch_vars = [test_model.backbone.output_dict['feature']]
        export_onnx(path, feed_vars, fetch_vars, exe, program=test_program)
    else:
        feed_vars = [test_model.backbone.input_dict['image']]
        fetch_vars = [test_model.backbone.output_dict['feature']]
        paddle.static.save_inference_model(
            path, feed_vars, fetch_vars, exe, program=test_program)
    print("Save exported model to '{}'.".format(args.output_dir))
