chia_sdk_driver/layers/datalayer/
writer_layer.rsuse chia_puzzles::standard::StandardSolution;
use chia_sdk_types::Conditions;
use clvm_traits::{clvm_quote, FromClvm, ToClvm};
use clvm_utils::{CurriedProgram, ToTreeHash, TreeHash};
use clvmr::{Allocator, NodePtr};
use hex_literal::hex;
use crate::{DriverError, Layer, Puzzle, Spend, SpendContext, StandardLayer};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WriterLayer<I> {
    pub inner_puzzle: I,
}
impl<I> WriterLayer<I> {
    pub fn new(inner_puzzle: I) -> Self {
        Self { inner_puzzle }
    }
}
impl<I> Layer for WriterLayer<I>
where
    I: Layer,
{
    type Solution = I::Solution;
    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
        let Some(puzzle) = puzzle.as_curried() else {
            return Ok(None);
        };
        if puzzle.mod_hash != WRITER_FILTER_PUZZLE_HASH {
            return Ok(None);
        }
        let args = WriterLayerArgs::<NodePtr>::from_clvm(allocator, puzzle.args)?;
        let Some(inner_puzzle) =
            I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
        else {
            return Ok(None);
        };
        Ok(Some(Self { inner_puzzle }))
    }
    fn parse_solution(
        allocator: &Allocator,
        solution: NodePtr,
    ) -> Result<Self::Solution, DriverError> {
        let inner_solution =
            WriterLayerSolution::<NodePtr>::from_clvm(allocator, solution)?.inner_solution;
        I::parse_solution(allocator, inner_solution)
    }
    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
        let curried = CurriedProgram {
            program: ctx.delegated_writer_filter()?,
            args: WriterLayerArgs::new(self.inner_puzzle.construct_puzzle(ctx)?),
        };
        ctx.alloc(&curried)
    }
    fn construct_solution(
        &self,
        ctx: &mut SpendContext,
        solution: Self::Solution,
    ) -> Result<NodePtr, DriverError> {
        let inner_solution = self.inner_puzzle.construct_solution(ctx, solution)?;
        ctx.alloc(&WriterLayerSolution::<NodePtr> { inner_solution })
    }
}
impl<I> ToTreeHash for WriterLayer<I>
where
    I: ToTreeHash,
{
    fn tree_hash(&self) -> TreeHash {
        let inner_puzzle_hash = self.inner_puzzle.tree_hash();
        WriterLayerArgs::curry_tree_hash(inner_puzzle_hash)
    }
}
impl WriterLayer<StandardLayer> {
    pub fn spend(
        self,
        ctx: &mut SpendContext,
        output_conditions: Conditions,
    ) -> Result<Spend, DriverError> {
        let dp = ctx.alloc(&clvm_quote!(output_conditions))?;
        let solution = self.construct_solution(
            ctx,
            StandardSolution {
                original_public_key: None,
                delegated_puzzle: dp,
                solution: NodePtr::NIL,
            },
        )?;
        let puzzle = self.construct_puzzle(ctx)?;
        Ok(Spend { puzzle, solution })
    }
}
pub const WRITER_FILTER_PUZZLE: [u8; 110] = hex!(
    "
    ff02ffff01ff02ff02ffff04ff02ffff04ffff02ff05ff0b80ff80808080ffff04ffff01ff02ffff
    03ff05ffff01ff02ffff03ffff09ff11ffff0181f380ffff01ff0880ffff01ff04ff09ffff02ff02
    ffff04ff02ffff04ff0dff808080808080ff0180ff8080ff0180ff018080
    "
);
pub const WRITER_FILTER_PUZZLE_HASH: TreeHash = TreeHash::new(hex!(
    "
    407f70ea751c25052708219ae148b45db2f61af2287da53d600b2486f12b3ca6
    "
));
#[derive(ToClvm, FromClvm, Debug, Clone, Copy, PartialEq, Eq)]
#[clvm(curry)]
pub struct WriterLayerArgs<I> {
    pub inner_puzzle: I,
}
impl<I> WriterLayerArgs<I> {
    pub fn new(inner_puzzle: I) -> Self {
        Self { inner_puzzle }
    }
}
impl WriterLayerArgs<TreeHash> {
    pub fn curry_tree_hash(inner_puzzle: TreeHash) -> TreeHash {
        CurriedProgram {
            program: WRITER_FILTER_PUZZLE_HASH,
            args: WriterLayerArgs { inner_puzzle },
        }
        .tree_hash()
    }
}
#[derive(ToClvm, FromClvm, Debug, Clone, PartialEq, Eq)]
#[clvm(list)]
pub struct WriterLayerSolution<I> {
    pub inner_solution: I,
}
impl SpendContext {
    pub fn delegated_writer_filter(&mut self) -> Result<NodePtr, DriverError> {
        self.puzzle(WRITER_FILTER_PUZZLE_HASH, &WRITER_FILTER_PUZZLE)
    }
}