# -*- coding: utf-8 -*-
import sys, os, json, traceback
from datetime import datetime

try:
    import pygame
except Exception:
    print("pygame 导入失败")
    traceback.print_exc()
    sys.exit(1)

def log(*a):
    print("[BLOG]", *a)
    sys.stdout.flush()

log("Python", sys.version.split()[0])
log("pygame", pygame.version.ver)

# ---------- 初始化 ----------
try:
    pygame.init()
    pygame.font.init()
    log("pygame 初始化成功")
except Exception:
    traceback.print_exc()
    sys.exit(1)

WIDTH, HEIGHT = 900, 650
try:
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("My Pygame Blog")
    log("窗口创建成功")
except Exception:
    traceback.print_exc()
    sys.exit(1)

# ---------- 字体（完全防御式） ----------
def get_font(size, bold=False):
    # 1) 尝试字体文件
    for path in [
        "C:/Windows/Fonts/msyh.ttc",
        "C:/Windows/Fonts/simhei.ttf",
        "C:/Windows/Fonts/simsun.ttc",
        "C:/Windows/Fonts/arial.ttf",
        "/System/Library/Fonts/PingFang.ttc",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
    ]:
        try:
            if os.path.exists(path):
                f = pygame.font.Font(path, size)
                if bold:
                    f.set_bold(True)
                log("字体加载:", path)
                return f
        except Exception as e:
            log("字体失败:", path, repr(e))
    # 2) 兜底默认字体
    try:
        f = pygame.font.Font(None, size)
        if bold:
            f.set_bold(True)
        log("使用默认字体")
        return f
    except Exception:
        traceback.print_exc()
        sys.exit(1)

FONT_TITLE = get_font(36, True)
FONT_H2    = get_font(24, True)
FONT_BODY  = get_font(18)
FONT_SMALL = get_font(14)

# ---------- 配色 ----------
BG      = (245, 246, 250)
CARD    = (255, 255, 255)
PRIMARY = (60, 120, 216)
TEXT    = (33, 37, 41)
MUTED   = (120, 128, 140)
BORDER  = (222, 226, 230)
HOVER   = (235, 241, 252)

# ---------- 数据 ----------
DATA_FILE = "posts.json"
DEFAULT_POSTS = [
    {"title": "Welcome", "content": "Hello Pygame Blog!\nClick + New to add a post.",
     "date": "2024-01-01 10:00"},
    {"title": "About", "content": "This is a Pygame desktop blog demo.",
     "date": "2024-01-02 14:30"},
]

def load_posts():
    try:
        if os.path.exists(DATA_FILE):
            with open(DATA_FILE, "r", encoding="utf-8") as f:
                data = json.load(f)
                if isinstance(data, list) and data:
                    return data
    except Exception as e:
        log("读取失败:", repr(e))
    return [dict(p) for p in DEFAULT_POSTS]

def save_posts(ps):
    try:
        with open(DATA_FILE, "w", encoding="utf-8") as f:
            json.dump(ps, f, ensure_ascii=False, indent=2)
        log("已保存")
    except Exception as e:
        log("保存失败:", repr(e))

posts = load_posts()
log("加载文章数:", len(posts))

# ---------- 控件 ----------
class Button:
    def __init__(self, rect, text, color=PRIMARY, text_color=(255,255,255), font=None):
        self.rect = pygame.Rect(rect)
        self.text = text
        self.color = color
        self.text_color = text_color
        self.font = font or FONT_BODY
        self.hover = False

    def draw(self, surf):
        col = self.color
        if self.hover:
            col = tuple(min(255, c + 25) for c in col)
        pygame.draw.rect(surf, col, self.rect, border_radius=8)
        t = self.font.render(self.text, True, self.text_color)
        surf.blit(t, t.get_rect(center=self.rect.center))

    def handle(self, event):
        if event.type == pygame.MOUSEMOTION:
            self.hover = self.rect.collidepoint(event.pos)
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            if self.rect.collidepoint(event.pos):
                return True
        return False


class TextBox:
    def __init__(self, rect, placeholder="", multiline=False):
        self.rect = pygame.Rect(rect)
        self.text = ""
        self.placeholder = placeholder
        self.active = False
        self.multiline = multiline

    def _wrap(self, text, max_w):
        out = []
        for raw in text.split("\n"):
            if not raw:
                out.append("")
                continue
            line = ""
            for ch in raw:
                if FONT_BODY.size(line + ch)[0] <= max_w:
                    line += ch
                else:
                    out.append(line)
                    line = ch
            out.append(line)
        return out

    def draw(self, surf):
        col = PRIMARY if self.active else BORDER
        pygame.draw.rect(surf, (255,255,255), self.rect, border_radius=6)
        pygame.draw.rect(surf, col, self.rect, 2, border_radius=6)
        lines = self._wrap(self.text if self.text else self.placeholder,
                           self.rect.width - 16)
        color = TEXT if self.text else MUTED
        y = self.rect.y + 8
        for line in lines:
            if y + 20 > self.rect.bottom:
                break
            surf.blit(FONT_BODY.render(line, True, color), (self.rect.x + 8, y))
            y += 22
        if self.active and pygame.time.get_ticks() % 1000 < 500 and lines:
            last = lines[-1]
            w = FONT_BODY.size(last)[0]
            cx = self.rect.x + 8 + w
            cy = self.rect.y + 8 + (len(lines) - 1) * 22
            pygame.draw.line(surf, TEXT, (cx, cy), (cx, cy + 20), 2)

    def handle(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            self.active = self.rect.collidepoint(event.pos)
        if event.type == pygame.KEYDOWN and self.active:
            try:
                if event.key == pygame.K_BACKSPACE:
                    self.text = self.text[:-1]
                elif event.key == pygame.K_RETURN:
                    if self.multiline:
                        self.text += "\n"
                elif event.unicode and event.unicode.isprintable():
                    self.text += event.unicode
            except Exception as e:
                log("输入异常:", repr(e))


# ---------- 状态 ----------
STATE_LIST, STATE_DETAIL, STATE_NEW = "list", "detail", "new"
state = STATE_LIST
current_index = 0
scroll = 0
SIDEBAR_W, HEADER_H = 280, 70

btn_new    = Button((WIDTH - 130, 18, 110, 36), "+ New")
btn_back   = Button((20, HEIGHT - 60, 100, 40), "< Back", color=MUTED)
btn_save   = Button((WIDTH - 140, HEIGHT - 60, 120, 40), "Publish")
btn_delete = Button((WIDTH - 240, HEIGHT - 60, 90, 40), "Delete", color=(220,70,70))

input_title   = TextBox((60, 130, WIDTH - 120, 44), "Title...")
input_content = TextBox((60, 200, WIDTH - 120, HEIGHT - 320), "Content...", multiline=True)


def draw_header(title):
    pygame.draw.rect(screen, (255,255,255), (0, 0, WIDTH, HEADER_H))
    pygame.draw.line(screen, BORDER, (0, HEADER_H), (WIDTH, HEADER_H), 1)
    screen.blit(FONT_H2.render(title, True, TEXT), (30, 20))


def draw_sidebar():
    pygame.draw.rect(screen, (255,255,255), (0, HEADER_H, SIDEBAR_W, HEIGHT - HEADER_H))
    pygame.draw.line(screen, BORDER, (SIDEBAR_W, HEADER_H), (SIDEBAR_W, HEIGHT), 1)
    y = HEADER_H + 16
    mx, my = pygame.mouse.get_pos()
    for i, p in enumerate(posts):
        r = pygame.Rect(10, y - scroll, SIDEBAR_W - 20, 70)
        if r.bottom < HEADER_H or r.top > HEIGHT:
            y += 80; continue
        if (state == STATE_DETAIL and i == current_index) or r.collidepoint((mx,my)):
            pygame.draw.rect(screen, HOVER, r, border_radius=8)
        title = p.get("title", "")[:20]
        screen.blit(FONT_BODY.render(title, True, TEXT), (22, y + 8))
        screen.blit(FONT_SMALL.render(p.get("date", ""), True, MUTED), (22, y + 40))
        y += 80


def draw_list():
    draw_header("My Blog")
    draw_sidebar()
    x0 = SIDEBAR_W + 40
    y = HEADER_H + 40
    if not posts:
        screen.blit(FONT_BODY.render("No posts yet.", True, MUTED), (x0, y))
        return
    mx, my = pygame.mouse.get_pos()
    for i, p in enumerate(posts):
        card = pygame.Rect(x0, y - scroll, WIDTH - x0 - 40, 110)
        if card.bottom < HEADER_H or card.top > HEIGHT:
            y += 130; continue
        hover = card.collidepoint((mx, my))
        pygame.draw.rect(screen, CARD, card, border_radius=10)
        pygame.draw.rect(screen, PRIMARY if hover else BORDER, card, 2, border_radius=10)
        screen.blit(FONT_H2.render(p.get("title",""), True, TEXT), (card.x+20, card.y+15))
        screen.blit(FONT_SMALL.render(p.get("date",""), True, MUTED), (card.x+20, card.y+55))
        summary = p.get("content","").replace("\n"," ")[:50]
        screen.blit(FONT_BODY.render(summary, True, MUTED), (card.x+20, card.y+78))
        y += 130


def draw_detail():
    draw_header("Detail")
    draw_sidebar()
    if not posts: return
    p = posts[current_index]
    x0 = SIDEBAR_W + 40
    y = HEADER_H + 30
    screen.blit(FONT_TITLE.render(p.get("title",""), True, TEXT), (x0, y)); y += 55
    screen.blit(FONT_SMALL.render(p.get("date",""), True, MUTED), (x0, y)); y += 30
    pygame.draw.line(screen, BORDER, (x0, y), (WIDTH - 40, y), 1); y += 20
    max_w = WIDTH - x0 - 40
    for raw in p.get("content","").split("\n"):
        if not raw:
            y += 24; continue
        line = ""
        for ch in raw:
            if FONT_BODY.size(line + ch)[0] <= max_w:
                line += ch
            else:
                screen.blit(FONT_BODY.render(line, True, TEXT), (x0, y))
                y += 26; line = ch
        screen.blit(FONT_BODY.render(line, True, TEXT), (x0, y))
        y += 26
    btn_back.draw(screen)
    btn_delete.draw(screen)


def draw_new():
    draw_header("New Post")
    input_title.draw(screen)
    input_content.draw(screen)
    btn_save.draw(screen)
    btn_back.draw(screen)


# ---------- 主循环 ----------
clock = pygame.time.Clock()
running = True
frame = 0

log("进入主循环")

while running:
    try:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

            if state == STATE_LIST:
                if btn_new.handle(event):
                    input_title.text = ""
                    input_content.text = ""
                    state = STATE_NEW
                if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                    x0 = SIDEBAR_W + 40
                    y = HEADER_H + 40
                    for i in range(len(posts)):
                        card = pygame.Rect(x0, y - scroll, WIDTH - x0 - 40, 110)
                        if card.collidepoint(event.pos):
                            current_index = i
                            state = STATE_DETAIL
                            break
                        y += 130
                # MOUSEWHEEL 兼容
                if hasattr(pygame, "MOUSEWHEEL") and event.type == pygame.MOUSEWHEEL:
                    scroll -= event.y * 40
                    scroll = max(0, scroll)

            elif state == STATE_DETAIL:
                if btn_back.handle(event):
                    state = STATE_LIST
                    scroll = 0
                if btn_delete.handle(event) and posts:
                    posts.pop(current_index)
                    save_posts(posts)
                    state = STATE_LIST

            elif state == STATE_NEW:
                input_title.handle(event)
                input_content.handle(event)
                if btn_back.handle(event):
                    state = STATE_LIST
                if btn_save.handle(event):
                    if input_title.text.strip():
                        posts.insert(0, {
                            "title": input_title.text.strip(),
                            "content": input_content.text.strip() or "(empty)",
                            "date": datetime.now().strftime("%Y-%m-%d %H:%M"),
                        })
                        save_posts(posts)
                        state = STATE_LIST
                        scroll = 0

        screen.fill(BG)
        if state == STATE_LIST:
            draw_list()
            btn_new.draw(screen)
        elif state == STATE_DETAIL:
            draw_detail()
        elif state == STATE_NEW:
            draw_new()

        pygame.display.flip()
        clock.tick(60)
        frame += 1

    except Exception:
        log("主循环异常！")
        traceback.print_exc()
        running = False

log("退出，共运行", frame, "帧")
pygame.quit()
sys.exit()